Рубріки: Теория

Основы Make и Makefile, а также примеры их использования

Сергій Бондаренко

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

Один из таких инструментов, проверенных временем, — утилита Make. Ее стандарт Makefile широко используется различными системами автоматизации сборки Automake, CMake, imake, qmake, nmake, wmake, Apache Ant, Apache Maven, OpenMake Meister, Gradle. В этой статье мы обсудим базовые возможности, которые нужно знать всем для начала уверенной работы с Make/Makefile.

1. История возникновения Make

Утилита под названием Make возникла еще в далеком 1976 году в стенах компании Bell Labs. Появилась она в результате визита Стива Джонсона автора yacc — стандартной утилиты для BSD и AT&T Unix.

Как-то раз он ворвался в офис, где работал Стюарт Фельдман, громко проклиная все на свете за то, что все утро у него ушло на отладку довольно простой программы.

Исполняемый файл с изменениями по какой-то причине не обновлялся . Поскольку за день до этого Фельдман столкнулся с аналогичной проблемой в своем проекте, ему пришла в голову идея создать универсальный инструмент для решения подобных задач.

Вначале это была просто продуманная идея анализатора зависимостей, но в конечном итоге программа стала более простой и дружелюбной. До появления системы компиляции Make в Unix использовались самописные кустарные сценарии командной строки, применяемые к исходному коду приложений.

2. Варианты утилиты Make

Сейчас есть целый ряд программ автоматизаторов сборки, которые отслеживают зависимости в файлах, однако Make — одна из самых распространенных. В первую очередь по причине того, что она включена в дистрибутивы GNU/Linux.

Вообще есть две версии этой программы. Первая — написанная под платформу BSD впоследствии перекочевавшая на FreeBSD, NetBSD и OpenBSD. Она основана на разработке Адама де Бура с возможностью параллельной сборки. Вторая версия — GNU Make, предназначена для использования в Linux и MacOS.

Для платформы Microsoft Windows имеется аналогичное приложение — nmake.

3. Что означает Makefile

Программа 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-файлов — это полный по Тьюрингу декларативный язык. В нем описаны необходимые конечные условия, но порядок, в котором должны выполняться действия, не важен, что иногда сбивает с толку программистов, привыкших к императивному программированию.

4. Правила языка для написания Make-файлов

Язык Make-файлов базируется на командах. Этими командами описываются цели. В качестве цели можно выбирать имя действия, которое необходимо осуществить, скажем, clean. Также под целью можно принимать имя исполняемого или объектного файла.

Суть построения такого файла состоит в грамотной группировке команд и в выборе понятных названий для целей. Общий синтаксис выглядит так:

# Makefile
цель1: # название_цели, поддерживается kebab-case (вариант при котором вместо символа подчеркивания используется дефис) и snake_case (стиль написания составных слов, при котором они разделяются символом подчеркивания)
    команда1 # Обратите внимание — все команды, обязаны содержать в начале символ табуляции — так инструмент сборки отслеживает правила и другие цели
    команда2 # если предыдущая команда была успешно выполнена, начинает выполняться следующая команда и так далее

Для команд применяются команды оболочки shell — они будут отображены в консоли. Когда необходимо, чтобы команды не показывались в командной строке, перед командой используется символ @. Если не вводить имя цели, то выполнится цель с именем all либо самая первая цель в сценарии. В качестве пререквизитов или зависимостей, как правило, используются другие цели, представляющие собой имена файлов.

5. Пишем файл Make для простого проекта

Разберем вариант сборки простого приложения на Си. Предположим, наше приложение 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:

  1. собирает из этих данных корректную цепочку команд для получения необходимых конечных файлов;
  2. запускает создание запланированного файла только в случае, когда такой файл отсутствует либо имеет более раннее время создания по сравнению с файлами, от которых зависит.

Бывает, что при запуске утилиты 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-файла на основе реального проекта:

 

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023