Как известно, программный код
Один из таких инструментов, проверенных временем, — утилита Make. Ее стандарт
Утилита под названием Make возникла еще в далеком 1976 году в стенах компании Bell Labs. Появилась она в результате визита Стива Джонсона
Как-то раз он ворвался в офис, где работал Стюарт Фельдман, громко проклиная все на свете за то, что все утро у него ушло на отладку довольно простой программы.
Исполняемый файл с изменениями по какой-то причине не обновлялся . Поскольку за день до этого Фельдман столкнулся с аналогичной проблемой в своем проекте, ему пришла в голову идея создать универсальный инструмент для решения подобных задач.
Вначале это была просто продуманная идея анализатора зависимостей, но в конечном итоге программа стала более простой и дружелюбной. До появления системы компиляции Make
в Unix
использовались самописные кустарные сценарии командной строки, применяемые к исходному коду приложений.
Сейчас есть целый ряд программ автоматизаторов сборки, которые отслеживают зависимости в файлах, однако Make
— одна из самых распространенных. В первую очередь по причине того, что она включена в дистрибутивы GNU/Linux.
Вообще есть две версии этой программы. Первая — написанная под платформу BSD
Для платформы Microsoft Windows имеется аналогичное приложение — nmake
.
Программа Make
может понадобиться, когда перед вами стоит задача обновления в сборке определенных данных.
Этот инструмент выполняет анализ файлов и идентифицирует, какие части большой программы необходимо перекомпилировать заново, и выдает соответствующие команды для их повторной сборки.
Для функционирования этой утилиты необходим специальный служебный файл, называемый Makefile
(или makefile
). Он описывает взаимосвязи между файлами в проекте, над которым вы работаете, и идентифицирует команды для обновления каждого файла. Makefile
помещается вместе с кодом в репозиторий
Содержимое простого Makefile
может иметь вид:
edit : main1.o kbd1.o command1.o display.o \ insert1.o search1.o files1.o utils1.o cc -o edit main1.o kbd1.o command1.o display1.o \ insert1.o search1.o files1.o utils1.o main1.o : main1.c defs1.h cc -c main1.c kbd1.o : kbd1.c defs.h command1.h cc -c kbd1.c command1.o : command1.c defs.h command1.h cc -c command1.c display1.o : display1.c defs1.h buffe1r.h cc -c display1.c insert1.o : insert1.c defs1.h buffer1.h cc -c insert1.c search1.o : search1.c defs1.h buffer1.h cc -c search1.c files1.o : files1.c defs1.h buffer1.h command1.h cc -c files1.c utils1.o : utils1.c defs.h cc -c utils1.c clean : rm edit main1.o kbd1.o command1.o display1.o \ insert1.o search1.o files1.o utils1.o
После запуска GNU Make считывает файл с именем GNUmakefile
, makefile
или Makefile
. Если необходимо изменить поведение программы при обработке файла по умолчанию, например, сделать так, чтобы открывался файл с другим именем, содержащий иной набор инструкций, следует указать команду make -f other_name_makefile
.
Язык make
-файлов — это полный по Тьюрингу декларативный язык. В нем описаны необходимые конечные условия, но порядок, в котором должны выполняться действия, не важен, что иногда сбивает с толку программистов, привыкших к императивному программированию.
Язык Make
-файлов базируется на командах. Этими командами описываются цели. В качестве цели можно выбирать имя действия, которое необходимо осуществить, скажем, clean
. Также под целью можно принимать имя исполняемого или объектного файла.
Суть построения такого файла состоит в грамотной группировке команд и в выборе понятных названий для целей. Общий синтаксис выглядит так:
# Makefile цель1: # название_цели, поддерживается kebab-case (вариант при котором вместо символа подчеркивания используется дефис) и snake_case (стиль написания составных слов, при котором они разделяются символом подчеркивания) команда1 # Обратите внимание — все команды, обязаны содержать в начале символ табуляции — так инструмент сборки отслеживает правила и другие цели команда2 # если предыдущая команда была успешно выполнена, начинает выполняться следующая команда и так далее
Для команд применяются команды оболочки shell
— они будут отображены в консоли. Когда необходимо, чтобы команды не показывались в командной строке, перед командой используется символ @
. Если не вводить имя цели, то выполнится цель с именем all
либо самая первая цель в сценарии. В качестве пререквизитов или зависимостей, как правило, используются другие цели, представляющие собой имена файлов.
Разберем вариант сборки простого приложения на Си. Предположим, наше приложение program
состоит из пары файлов кода – main.c
и lib.c
, а также одного заголовочного файла – defines.h
, что включен в оба файла-исходника кода. Тогда для создания program
необходимо из пар (main.c defines.h)
и (lib.c defines.h)
создать объектные файлы main.o
и lib.o
, а затем слинковать их в program
. При ручной сборке следует выполнить такие команды:
cc -c main.c defines.h cc -c lib.c defines.h cc -o program main.o lib.o
Если на каком-то этапе работы над приложением файл defines.h
подвергнется редактированию, возникнет необходимость перекомпилирования обоих файлов и нового линкования. При изменении одного lib.c
, перекомпиляцию main.о
делать не нужно.
Следовательно, для каждого файла, который мы должны получить в процессе компиляции, нужно указать на основе каких файлов, а также с помощью какой команды он создается.
Make
:Бывает, что при запуске утилиты Make
цель явно не задана. В такой ситуации начнет выполняться первая цель, имя которой не начинается с точки “.
“. Для нашей программы достаточно написать такой файл:
program: main.o lib.o cc -o program main.o lib.o main.o lib.o: defines.h
Есть ряд моментов, которые нужно обговорить относительно этого простого примера. В имени второй цели указаны два парных файла. Для этой же цели не указана команда компиляции. Кроме того, нигде явно не указана зависимость объектных файлов от файлов *.с
.
Дело в том, что программа Make
содержит встроенные правила для получения файлов с определенными расширениями. Так для цели – объектного файла (расширение *.o
) при нахождении соответствующего файла с расширением *.с
будет автоматически вызван компилятор «сс-с
» .
Нередко команды могут задействовать различные параметры конфигурации, определять пути, устанавливать переменные окружения. С помощью Make
можно всем этим управлять, прописав нужную переменную в команде. Формат описания переменной выглядит просто: переменная = значение
. Переменные могут быть только строками.
Вы можете использовать любые символы, включая пробелы.
# Makefile say: echo "Hello, $(HELLO)!" # Bash $ make say HELLO=CRAZY echo "Hello, CRAZY!" Hello, CRAZY! $ make say HELLO=HighloadToday echo "Hello, HighloadToday!" Hello, HighloadToday!
Теперь текст нашего примера можно модифицировать:
OBJ = main.o lib.o program: $(OBJ) cc -o program $(OBJ) $(OBJ): defines.h
Обработка значения переменных выполняется исключительно в момент их задействования all
на дисплей будет выведен текст «Ась?
»:
foo = $(bar) bar = $(ugh) ugh = Ась? all: echo $(foo)
Если в наш проект был добавлен еще один заголовочный файл lib.h
, включаемый только в lib.c
, — структура make
-файла изменится, будет дописана еще одна строка:
lib.o: lib.h
Таким образом, один целевой файл может быть указан сразу в нескольких целях. При этом полный список связей будет составлен из списков зависимостей всех целей, в которых он принимает участие, а создание файла будет выполняться только один раз.
Теперь вы знаете базовые принципы работы утилиты Make
и можете автоматизировать процедуру сборки. Для закрепления навыков рекомендуем вам посмотреть видео, в котором показан процесс создания Make
-файла на основе реального проекта:
Прокси (proxy), или прокси-сервер — это программа-посредник, которая обеспечивает соединение между пользователем и интернет-ресурсом. Принцип…
Согласитесь, было бы неплохо соединить в одно сайт и приложение для смартфона. Если вы еще…
Повсеместное распространение смартфонов привело к огромному спросу на мобильные игры и приложения. Миллиарды пользователей гаджетов…
В перечне популярных чат-ботов с искусственным интеллектом Google Bard (Gemini) еще не пользуется такой популярностью…
Скрипт (англ. — сценарий), — это небольшая программа, как правило, для веб-интерфейса, выполняющая определенную задачу.…
Дедлайн (от англ. deadline — «крайний срок») — это конечная дата стачи проекта или задачи…