Осваиваем сборку программы с помощью утилиты make

newbie_

Опубликован:  2019-11-01T08:24:37.330225Z

В одном из предыдущих выпусков блога я продемонстрировал начальный этап разработки микропроекта dice - простейшего консольного эмулятора игры в кости. Сегодня я рассмотрю автоматизированную сборку этой программы и напишу свой первый Makefile, на основе которого откомпилирую исполняемый файл программы при помощи утилиты GNU Make.

Исходный код микропроекта dice в настоящий момент состоит из трёх файлов, расположен в собственном одноимённом каталоге в моём рабочем пространстве и имеет следующую структуру:

AkFBVAOm0O.png

В каталоге src хранятся исходники, три файла: ddice.cpp, ddice.h и dice.cpp; а в каталоге bin - бинарный исполняемый файл dice, полученный в результате компиляции исходных кодов. До настоящего момента компиляция программы осуществлялась соответствующей командой:

g++ -o bin/dice src/ddice.cpp src/dice.cpp

Компиляция завершается успешно без ошибок и предупреждений, а исполненная в консоли программа работает без сбоев и недочётов.

dNd3PRWW0E.png

Вроде бы всё не плохо, но... Любой маленький проект всегда имеет шансы вырасти в большую, удобную, полезную и функциональную программу, а это значит, что и исходные коды этого проекта получат новые файлы, возможно, будут использовать новые сторонние библиотеки, а значит получат особые параметры сборки и линковки. Постоянно держать все эти параметры в голове и каждый раз для сборки проекта вводить довольно длинную команду компилятора - не очень хорошая идея, поэтому для сборки проекта стоит воспользоваться утилитой GNU Make, а значит необходимо освоить начальные навыки составления Makefile. Этим и займёмся сейчас.

Итак, для сборки проекта мне потребуется новый текстовый файл - Makefile. Расположу я его прямо в корне проекта.

geany Makefile

mPzqtvpPnR.png

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

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

Пишу в Makefile следующий код.

CC = g++
CFLAGS = -c -Wall

В проекте имеется два исходных файла: ddice.cpp и dice.cpp. Оба эти файла должны быть откомпилированы в одноимённые объектные файлы с расширением *.o. Для каждого файла необходимо создать в Makefile собственное правило. Справка GNU Make говорит, что правило может иметь следующую форму:

target ...: prerequisites ...
    recipe
    ...
    ...

При этом recipe должен всегда начинаться со знака табуляции, если не заданы отличные от дефолтных настройки. В данном случае target - это цель для сборки, prerequisites будет содержать перечисление исходников, из которых собирается цель, а recipe - команда для сборки цели, как видно, команд у цели может быть несколько. Для своего простейшего проекта на текущем этапе разработки я определяю в качестве целей два объектных файла.

bin/ddice.o: src/ddice.h src/ddice.cpp
    $(CC) $(CFLAGS) src/ddice.cpp -o bin/ddice.o

bin/dice.o: src/ddice.h src/dice.cpp
    $(CC) $(CFLAGS) src/dice.cpp -o bin/dice.o

После того, как оба объектных файла будут откомпилированы, необходимо их слинковать в исполняемый файл. Добавляю ещё одно правило.

bin/dice: bin/ddice.o bin/dice.o
    $(CC) bin/ddice.o bin/dice.o -o bin/dice

Следует обратить внимание, что компиляция и линковка осуществляются с разными опциями компилятора, в процессе разработки проекта эти опции будут дополняться и, возможно, в файле появятся новые переменные. Пока же проект довольно мал, поэтому мне осталось добавить ещё пару правил.

all: bin/dice

clean:
    rm -f bin/*.o bin/dice

Правило all осуществляет сборку заданной цели при запуске make без параметров, и здесь я указал в prerequisites ключевое правило для компиляции проекта. Как видно, это правило не содержит recipe. В файле это правило должно располагаться первым, сразу после перечисления переменных.

Правило clean необходимо, чтобы почистить проект от бинарных файлов. В процессе разработки здесь может появиться больше команд.

Проект небольшой, поэтому и Makefile получился весьма простой и незамысловатый. Сохраняю файл и пробую собрать проект.

make

RLY4GDiQys.png

Отлично, цель достигнута, теперь для компиляции проекта достаточно простой команды, и не потребуется запоминать все опции компиляции и линковки, они все собраны в одном месте - Makefile.

Если никакие исходные файлы не изменялись, то повторная сборка проекта не потребует компиляции объектных файлов и соответственно линковки.

gYkpW3HuVX.png

Компиляция будет осуществляться только тогда, когда есть изменения в исходных файлах проекта. Это ещё одна достигнутая цель - автоматизация выборочной компиляции файлов и сборки проекта.

Рассмотренный пример Makefile-а довольно примитивен, но на начальном этапе изучения программирования достаточно полезным будет даже такой простой пример. Что ж.., попробую исполнить полученный файл dice.

bin/dice -n 3 Fossman SSkain

o8N0xSHM2j.png

Программа отработала в штатном режиме. Код проекта доступен на github.com.

Комментарии: