Написание makefile. Смотреть что такое "Make" в других словарях. Самый простой Мейкфайл

Написание makefile. Смотреть что такое "Make" в других словарях. Самый простой Мейкфайл

18.03.2019

Столкнулся на днях с непонятной ошибкой. Ставлю минимальную версию Freebsd 10.1 на hyperv. После установки указываю через bsdconfig на единственном интерфейсе hn0 получать настройки от dhcp. Все в порядке, настройки получены, занимаюсь конфигурированием сервера. Потом перезагружаюсь. Во время загрузки сервер Freebsd задумывается 30 секунд на моменте прописки дефолтного шлюза, потом продолжает загрузку. Захожу в систему и вижу, что ip адрес не получен.

Начинаю разбираться в чем дело. Проверяю /etc/rc.conf:

# cat /etc/rc.conf hostname="hyperv-freebsd" ifconfig_hn0="DHCP"

Все, больше ничего нет. Ошибиться негде. Все должно работать, но не работает. Если вручную запустить dhcp клиент:

# /sbin/dhclient -c /etc/dhclient.conf hn0

то сетевые настройки благополучно получаются.

Я решил добавить настройки dhclient в rc.conf:

Dhclient_program="/sbin/dhclient" dhclient_flags=""

Перезагружаюсь, результата нет, настройки по dhcp опять не получены. Очень странная ситуация, я, честно говоря, тут призадумался. Десятки раз настраивал freebsd, но такое вижу впервые. Делать нечего, решил заглянуть в Handbook. Иду в раздел Автоматическая настройка сети (DHCP) и читаю. Нахожу там интересный момент:

По умолчанию, конфигурирование FreeBSD по протоколу DHCP выполняется фоновым процессом, или асинхронно. Остальные стартовые скрипты продолжают работу не ожидая завершения процесса конфигурирования, тем самым ускоряя загрузку системы.

Фоновое конфигурирование не создает проблем в случае, если сервер DHCP быстро отвечает на запросы, и процесс конфигурирования происходит быстро. Однако, в некоторых случаях настройка по DHCP может длиться значительное время. При этом запуск сетевых сервисов может потерпеть неудачу, если будет произведен ранее завершения конфигурирования по DHCP. Запуск DHCP в синхронном режиме предотвращает проблему, откладывая выполнение остальных стартовых скриптов до момента завершения конфигурирования по DHCP.

Для откладывания запуска стартовых скриптов до завершения конфигурирования по DHCP (синхронный режим), укажите значение SYNCDHCP:

ifconfig_fxp0=»SYNCDHCP»

Стоит попробовать. Указываю в /etc/rc.conf:

Ifconfig_hn0="SYNCDHCP"

Перезагружаюсь и вуаля, все работает. То ли это особенность виртуальной машины на hyperv, то ли что-то еще, не знаю. Впервые с подобной ситуацией сталкиваюсь. Но решение найдено. Люблю я Freebsd за подробный handbook, но последнее время работаю с фрюшкой все реже и реже в силу различных причин, главная из которых отсутствие нужных дистрибутивов софта под данную систему. Производители программ все реже и реже включают поддержку Freebsd, а жаль.

Помогла статья? Есть возможность автора

Дополнительные материалы по Freebsd

Онлайн курс "Администратор Linux"

Если у вас есть желание научиться строить и поддерживать высокодоступные и надежные системы, рекомендую познакомиться с онлайн-курсом «Администратор Linux» в OTUS. Курс не для новичков, для поступления нужны базовые знания по сетям и установке Linux на виртуалку. Обучение длится 5 месяцев, после чего успешные выпускники курса смогут пройти собеседования у партнеров. Проверьте себя на вступительном тесте и смотрите программу детальнее по.
Рекомендую полезные материалы по :
  • Установка
  • Настройка
  • Обновление
  • Прокси сервер
  • Веб сервер NGINX
  • Веб сервер Apache

Описание на одиночный диск, либо на софтовый raid1, сделанный средствами zfs, которые поддерживает стандартный установщик.

У вас, вероятно, появился вопрос: можно ли не компилировать эти файлы по отдельности, а собрать сразу всю программу одной командой? Можно.

gcc calculate.c main.c -o kalkul -lm

Вы скажете, что это удобно? Удобно для нашей программы, потому что она состоит всего из двух c-файлов. Однако профессиональная программа может состоять из нескольких десятков таких файлов. Каждый раз набирать названия их всех в одной строке было бы делом чрезмерно утомительным. Но есть возможность решить эту проблему. Названия всех исходных файлов и все команды для сборки программы можно поместить в отдельный текстовый файл. А потом считывать их оттуда одной короткой командой.

Давайте создадим такой текстовый файл и воспользуемся им. В каталоге проекта kalkul2 удалите все файлы, кроме calculate.h, calculate.c, main.c. Затем создайте в этом же каталоге новый файл, назовите его Makefile (без расширений). Поместите туда следующий текст.

Kalkul: calculate.o main.o gcc calculate.o main.o -o kalkul -lm calculate.o: calculate.c calculate.h gcc -c calculate.c main.o: main.c calculate.h gcc -c main.c clean: rm -f kalkul calculate.o main.o install: cp kalkul /usr/local/bin/kalkul uninstall: rm -f /usr/local/bin/kalkul

Обратите внимание на строки, введённые с отступом от левого края. Этот отступ получен с помощью клавиши Tab. Только так его и надо делать! Если будете использовать клавишу «Пробел», команды не будут исполняться.

Затем дадим команду, состоящую всего из одного слова:

И сразу же в нашем проекте появляются и объектные файлы и запускаемый. Программа make как раз и предназначена для интерпретации команд, находящихся в файле со стандартным названием Makefile. Рассмотрим его структуру.

Makefile является списком правил. Каждое правило начинается с указателя, называемого «Цель». После него стоит двоеточие, а далее через пробел указываются зависимости. В нашем случае ясно, что конечный файл kalkul зависит от объектных файлов calculate.o и main.o. Поэтому они должны быть собраны прежде сборки kalkul. После зависимостей пишутся команды. Каждая команда должна находиться на отдельной строке, и отделяться от начала строки клавишей Tab. Структура правила Makefile может быть очень сложной. Там могут присутствовать переменные, конструкции ветвления, цикла. Этот вопрос требует отдельного подробного изучения.

Если мы посмотрим на три первых правила, то они нам хорошо понятны. Там те же самые команды, которыми мы уже пользовались. А что же означают правила clean, install и uninstall?

В правиле clean стоит команда rm, удаляющая исполняемый и объектные файлы. Флаг -f означает, что, если удаляемый файл отсутствует, программа должна это проигнорировать, не выдавая никаких сообщений. Итак, правило clean предназначено для «очистки» проекта, приведения его к такому состоянию, в каком он был до команды make.

Запустите

Появились объектные файлы и исполняемый. Теперь

Объектные и исполняемый файлы исчезли. Остались только c-файлы, h-файл и сам Makefile. То есть, проект «очистился» от результатов команды make.

Правило install помещает исполняемый файл в каталог /usr/local/bin - стандартный каталог размещения пользовательских программ. Это значит, что её можно будет вызывать из любого места простым набором её имени. Но помещать что-либо в этот каталог можно только, зайдя в систему под «суперпользователем». Для этого надо дать команду su и набрать пароль «суперпользователя». В противном случае система укажет, что вам отказано в доступе. Выход из «суперпользователя» осуществляется командой exit. Итак,

Теперь вы можете запустить это программу просто, введя имя программы, без прописывания пути.

Можете открыть каталог /usr/local/bin. Там должен появиться файл с названием kalkul.

Давайте теперь «уберём за собой», не будем засорять систему.

Посмотритекаталог /usr/local/bin. Файл kalkul исчез. Итак, правило uninstall удаляет программу из системного каталога.

Дмитрий Пантелеичев (dimanix2006 at rambler dot ru) - Make-файлы

Утилита автоматически определяет, какие части большой программы должны быть перекомпилированы и команды для их перекомпиляции. Наиболее часто make используется для компиляции C-программ и содержит особенности ориентированные именно на такие задачи, но можно использовать make с любым языком программирования. Более того, применение утилиты make не ограничивается программами. Можно использовать еe для описания любой задачи, где некоторые файлы должны автоматически порождаться из других всегда, когда те изменяются.

make-file

Прежде чем использовать make , необходимо создать файл, называемый make-файлом , который описывает отношения между файлами Вашей программы и содержит команды для обновления каждого файла. Обычно исполняемый файл зависит от объектных файлов, которые, в свою очередь, зависят от исходных файлов и файлов заголовков. Для имени make-файла рекомендуется название GNUmakefile , makefile или Makefile , причем поиск идет именно в указанном порядке. Если необходимо использовать нестандартное имя, то его можно передать явно через опцию -f .
Когда make-файл уже написан, достаточно выполнить в каталоге в котором он находится команду make . Простой make-файл состоит из правил(инструкций) следующего вида:


ПЕРЕМЕННАЯ = ЗНАЧЕНИЕ...
ЦЕЛЬ... : ЗАВИСИМОСТЬ...
КОМАНДА 1
КОМАНДА 2
ПЕРЕМЕННАЯ = ЗНАЧЕНИЕ...
ЦЕЛЬ... : ЗАВИСИМОСТЬ...
КОМАНДА 1
КОМАНДА 2

и т.д.

ЦЕЛЬ обычно представляет собой имя файла, генерируемого программой make ; примерами целей являются исполняемые или объектные файлы. Цель может также быть именем выполняемого действия, как, например, "clean ".
ЗАВИСИМОСТЬ - это файл, изменение которого служит признаком необходимости цели. Часто цель зависит от нескольких файлов.
КОМАНДА - это действие, которое выполняет make . Правило может иметь более чем одну команду - каждую на своей собственной строке. Важное замечание: необходимо начинать каждую строку, содержащую команды, с символа табуляции. Длинные строки разбиваются на несколько с использованием обратного слэша, за которым следует перевод строки. Знак диез # является началом комментария. Строка с # до конца игнорируется. Комментарии могут переноситься на несколько строк с помощью обратного слэша в конце строки.

Пример makefile

Использование действий по умолчанию


#default target - file edit
edit: main.o kbd.o command.o display.o \
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

Main.o: main.c defs.h
cc -c main.c
kbd.o: kbd.c defs.h command.h
cc -c kbd.c
command.o: command.c defs.h command.h
cc -c command.c
display.o: display.c defs.h buffer.h
cc -c display.c
insert.o: insert.c defs.h buffer.h
cc -c insert.c
search.o: search.c defs.h buffer.h
cc -c search.c
files.o: files.c defs.h buffer.h command.h
cc -c files.c
utils.o: utils.c defs.h
cc -c utils.c
clean:
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

По умолчанию, make начинает с первого правила (не считая правил, имена целей у которых начинаются с ". "). Это называется главной целью по умолчанию. В нашем случае это правило edit . Если файл edit новее чем объектные файлы от которых он зависит, то ничего не произойдет. В противном случае, прежде чем make сможет полностью обработать это правило, он должен рекурсивно обработать правила для файлов, от которых зависит "edit ". Каждый из этих файлов обрабатывается в соответствии со своими собственным правилом. Перекомпиляция должна быть проведена, если исходный файл или любой из заголовочных файлов, упомянутых среди зависимостей, обновлен позднее, чем объектный файл, или если объектный файл не существует.
Правилу clean не соответствует никакого создаваемого файла и, соответственно, clean ни от чего не зависит и само не входит в список зависимостей. При запуске по умолчанию clean вызываться не будет. Для его выполнения необходимо явно указать цель при запуске make - make clean.
Для сокращения записи можно использовать переменные и действия по умолчанию (неявные правила)

Специальная цель .PHONY является встроенной в make и определяет свои зависимости как цели-имена, которым нет соответствия в виде файлов. Если данное правило пропустить, то создание в текущем каталоге файла с именем clean заблокирует выполнение make clean .
Использование правил по умолчанию позволяет изменить стиль записей зависимостей:

Квадратные скобки означают необязательность присутствия данной части.
Цель - имя цели, которую надо выполнить.
Переменная ="abc" -переопределение переменных. Значения переменных введенных в командной строке имеют больший приоритет, чем определения в make-файле.
Опции:
-f file - явное задание имени make-файла , если задание опущено, то ищются файлы GNUmakefile , makefile или Makefile
-n ; - имитация действий без реального выполнения, служит для отладки
-t - изменение времени модификации цели без реального выполнения
-q - проверка на необходимость обновления цели без реального выполнения

Что такое Makefile? Makefile - это сценарий для утилиты make . Эта утилита помогает автоматизировать процесс компиляции проекта (проектов):

  • позволяет компилировать большие проекты, состоящие из большого количества библиотек;
  • определяет, что нужно компилировать (если файлы не изменялись, то не компилируются при очередной сборке проекта);
  • указывает необходимые опции компилятора (например, пути к файлам заголовков, оптимизация и т.д.);
  • выполняет дополнительный команды (например, удаление файлов);
  • поддерживает различными платформами и средами разработки.

По умолчанию утилита make считает, что файл сценариев называется Makefile, поэтому компиляцию можно запустить простым вызовом make в одной директории в Makefile. Если же имя файла сценария отличается, то нужно использовать явное указание его имени и ключ -f:

make -f prjmake.nmk

Makefile состоит из нескольких основных частей:

  • правила;
  • директивы;
  • переменные;
  • комментарии.
Начнем рассмотрение в обратном порядке списка - от простого к сложному.

Комментарии.
Комментарий обозначается символом "#". Если нужно использовать символ "#" в другом контексте, то необходимо добавить обратный слэш: "\#".

Перменные.
Сделать файл сценариев более простым и читабельным можно используя переменные. В терминологии make-файлов, переменные являются синонимами макроопределений. Создать переменную очень просто:

LIB = lib1.o lib2.o lib3.o

Название переменной задается в верхнем регистре (по негласному соглашению). Для получения параметров переменной используется смпользуются скобки и символ "
$(PATH)

Значение переменной может вычисляться рекурсивно:

PATH=$(PATH)/trunc

Либо можно использовать другую форму записи:

PATH+=/trunc

Некоторые переменные являются стандартыми константами, для них нельзя вычислять значение рекусивно. Такими перменными являются: CC (имя компилятора С), СХХ (имя компилятора С++), CFLAGS (параметры С компилятора), CXXFLAGS (параметры С++ компилятора).

Помимо переменных, определенных "писателем" файла, Make-файл предполагает использование автоматических переменных, значения которых вычисляются в контексте использования таких переменных (вычисление основывается на цели и зависимости правила - о целях и зависимостях ниже):

  • $@ - имя цели;
  • $$@ - имя цели, если его необходимо ввести в строке описания зависимости справа от двоеточия;
  • $? - имена всех зависимостей (с пробелами) которые новее, чем цель;
  • $^ - имена всез зависимостей с пробелами;
  • $* - имя текущего предусловия за вычетом суффикса;
  • $% - имя соответствующего.о файла, если текущей целью является файл библиотеки;
  • $** - только для nmake - внутри правила обозначаются все зависимости, оказавшиеся справа от двоеточия;
  • D - часть имени внутренних макроопределений, описывающая директорию файла (допустимые варианты применения: $(@D), $$(@D), $(
  • F - часть имени внутренних макоопределений, описывающая собственно имя файла (допустимые варианты прменения: $(@F), $$(@F), $(
  • B - часть имени внутренних макроопределений, описывающая базовое имя файла (без диска, директориии и расширения);
  • R - часть имени внутренних макроопределений, описывающая полный путь к файлу за вычетом расширения.
Директивы.
Директива - это команда для make с указанием, что делать в процессе чтения make-файла. Может выполнять такие действия:
  • подключать другие make-файлы;
  • принимать решение какие части файла использовать, а какие игнорировать (в зависимости от значения переменных);
  • определение значений переменных.
Поключать другие make-файлы можно директивой include. Она указвает утилите make , что нужно приостановить чтение текущего файла и прочитать указанные файлы:

include filenames...

Если фала не существует - выводиться ошибка. Для отключения сообщени об ошибке, к директиве добавлятеся префикс "-":

Include file1

Решение о том, какие части make-файла использовать, а какие игнорировать, примается на основе условий, синтаксис которого имеет вид:

conditional-directive
text-if-true
else
text-if-false
endif

Например:

libs_for_gcc = -lgnu
normal_libs =

foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
Определить значение перенной можно при помощи дериктив define... endef:

define two-lines
echo foo
echo $(bar)
endef
В этом случает значение переменной two-lines = echo foo; echo $(bar)

Правила.
Правила объясняют make , что и как нужно пересобирать. В общем виде структура выглядит так:

targets: prerequisites
command
...

Здесь targets - имена файлов-результатов, разделенные пробелами; так же может быть действие не связанное с процессом компиляции, например, clean. prerequisites - зависимости, то, от чего зависит создание targets. command - комманда, которая выполняется для получения targets; может быть не одна; перед командой обязательно ставиться табуляция.

Существуют несколько фиктивных целей:

  • all - выполнение работы по достижению всех частных целей, перечисленных ниже;
  • build - компиляция и сборка всех устаренших (невыполненных) целей/файлов;
  • clean - удаление всех файлов, кроме исходных;
  • docs - подготовка документации и размещение таких файлов в соответствующих системных директориях;
  • examples - компиляция и сборка примеров;
  • install - работа, по размещению всех готовых частей проекта в соответствующих директориях.
В именах файлов могут использоваться wildcards:

objects = *.o

Пример использования правил:

# задаем цель - исполняемый файл mytest, зависимый от lib.o
mytest: lib.o
# задаем комманду, результатом которой будет mytest
$(CXX) mytest.cpp $^ -o $@

# задаем цель - файл lib.o, зависимый от libimpl.h и libimpl.с
lib.o: libimpl*
# задаем комманду, результатом которой будет lib.o
$(CXX) -c $^ -o $@

Помимо обычных правил, можно создавать шаблонные правила - правила, применимые к группе файлов. Такие правила имеют суффиксную форму записи:

# все o-файлы зависят от cpp-файлов
.cpp.o:
gcc -c $^

Если бы в предыдущем примере mytest зависел от нескольких o-файлов, то шаблонное правило можно было бы записать так:

mytest: lib.o map.o vector.o
gcc $^ -o $@

Cpp.o:
gcc -c $^

Некоторый ключи компилятора:
-I"path/to/include" - директория со списком хидеров.
-Wall-Werror - вывод варнингов.
-O1, -O2, -O3 - оптимизация.

Некоторые ключи сборки:
-llibrary - указывает линковшику использовать библиотеку library при сборке программы.
-s - не включает симольные таблицы и информацию о размещении функций в испольняемый файл. Использование этого ключа позволяет существенно ужать исполняемые файлы.
-L"path/to/libs" - директория с библиотеками.
-static - статическая компоновка библиотек.

Мне в свое время очень не хватило подобной методички для понимания базовых вещей о make. Думаю, будет хоть кому-нибудь интересно. Хотя эта технология и отмирает, но все равно используется в очень многих проектах. Кармы на хаб «Переводы» не хватило, как только появится возможность - добавлю и туда. Добавил в Переводы. Если есть ошибки в оформлении, то прошу указать на них. Буду исправлять.

Статья будет интересная прежде всего изучающим программирование на C/C++ в UNIX-подобных системах от самых корней, без использования IDE.

Компилировать проект ручками - занятие весьма утомительное, особенно когда исходных файлов становится больше одного, и для каждого из них надо каждый раз набивать команды компиляции и линковки. Но не все так плохо. Сейчас мы будем учиться создавать и использовать Мейкфайлы. Makefile - это набор инструкций для программы make, которая помогает собирать программный проект буквально в одно касание.

Для практики понадобится создать микроскопический проект а-ля Hello World из четырех файлов в одном каталоге:

main.cpp

#include #include "functions.h" using namespace std; int main(){ print_hello(); cout << endl; cout << "The factorial of 5 is " << factorial(5) << endl; return 0; }


hello.cpp

#include #include "functions.h" using namespace std; void print_hello(){ cout << "Hello World!"; }


factorial.cpp

#include "functions.h" int factorial(int n){ if(n!=1){ return(n * factorial(n-1)); } else return 1; }


functions.h

void print_hello(); int factorial(int n);


Все скопом можно скачать отсюда
Автор использовал язык C++, знать который совсем не обязательно, и компилятор g++ из gcc. Любой другой компилятор скорее всего тоже подойдет. Файлы слегка подправлены, чтобы собирались gcc 4.7.1
Программа make
Если запустить
make
то программа попытается найти файл с именем по умолчание Makefile в текущем каталоге и выполнить инструкции из него. Если в текущем каталоге есть несколько мейкфайлов, то можно указать на нужный вот таким образом:
make -f MyMakefile
Есть еще множество других параметров, нам пока не нужных. О них можно узнать в ман-странице.
Процесс сборки
Компилятор берет файлы с исходным кодом и получает из них объектные файлы. Затем линковщик берет объектные файлы и получает из них исполняемый файл. Сборка = компиляция + линковка.
Компиляция руками
Самый простой способ собрать программу:
g++ main.cpp hello.cpp factorial.cpp -o hello
Каждый раз набирать такое неудобно, поэтому будем автоматизировать.
Самый простой Мейкфайл
В нем должны быть такие части:
цель: зависимости команда
Для нашего примера мейкфайл будет выглядеть так:
all: g++ main.cpp hello.cpp factorial.cpp -o hello
Обратите внимание, что строка с командой должна начинаться с табуляции! Сохраните это под именем Makefile-1 в каталоге с проектом и запустите сборку командой make -f Makefile-1
В первом примере цель называется all . Это цель по умолчанию для мейкфайла, которая будет выполняться, если никакая другая цель не указана явно. Также у этой цели в этом примере нет никаких зависимостей, так что make сразу приступает к выполнению нужной команды. А команда в свою очередь запускает компилятор.
Использование зависимостей
Использовать несколько целей в одном мейкфайле полезно для больших проектов. Это связано с тем, что при изменении одного файла не понадобится пересобирать весь проект, а можно будет обойтись пересборкой только измененной части. Пример:
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.cpp factorial.o: factorial.cpp g++ -c factorial.cpp hello.o: hello.cpp g++ -c hello.cpp clean: rm -rf *.o hello
Это надо сохранить под именем Makefile-2 все в том же каталоге

Теперь у цели all есть только зависимость, но нет команды. В этом случае make при вызове последовательно выполнит все указанные в файле зависимости этой цели.
Еще добавилась новая цель clean . Она традиционно используется для быстрой очистки всех результатов сборки проекта. Очистка запускается так: make -f Makefile-2 clean

Использование переменных и комментариев
Переменные широко используются в мейкфайлах. Например, это удобный способ учесть возможность того, что проект будут собирать другим компилятором или с другими опциями.
# Это комментарий, который говорит, что переменная CC указывает компилятор, используемый для сборки CC=g++ #Это еще один комментарий. Он поясняет, что в переменной CFLAGS лежат флаги, которые передаются компилятору CFLAGS=-c -Wall all: hello hello: main.o factorial.o hello.o $(CC) main.o factorial.o hello.o -o hello main.o: main.cpp $(CC) $(CFLAGS) main.cpp factorial.o: factorial.cpp $(CC) $(CFLAGS) factorial.cpp hello.o: hello.cpp $(CC) $(CFLAGS) hello.cpp clean: rm -rf *.o hello
Это Makefile-3
Переменные - очень удобная штука. Для их использования надо просто присвоить им значение до момента их использования. После этого можно подставлять их значение в нужное место вот таким способом: $(VAR)
Что делать дальше
После этого краткого инструктажа уже можно пробовать создавать простые мейкфайлы самостоятельно. Дальше надо читать серьезные учебники и руководства. Как финальный аккорд можно попробовать самостоятельно разобрать и осознать такой универсальный мейкфайл, который можно в два касания адаптировать под практически любой проект:
CC=g++ CFLAGS=-c -Wall LDFLAGS= SOURCES=main.cpp hello.cpp factorial.cpp OBJECTS=$(SOURCES:.cpp=.o) EXECUTABLE=hello all: $(SOURCES) $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(CC) $(LDFLAGS) $(OBJECTS) -o $@ .cpp.o: $(CC) $(CFLAGS) $< -o $@
Makefile-4
Успехов!

© 2024 beasthackerz.ru - Браузеры. Аудио. Жесткий диск. Программы. Локальная сеть. Windows