Разработка прикладных программ

Утилита make

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

Мощным средством для работы с большими программными комплексами в среде UNIX/Linux является утилита make. Она существенно облегчает перекомпиляцию программы при внесении изменений в отдельные файлы, в тех случаях, когда программа состоит из большого числа файлов. Однако, эта утилита весьма полезна даже в тех случаях, когда программа состоит из одного небольшого файла.

Рассмотрим, как работает команда make на простейшем примере. Предположим, что мы хотим получить бинарную программу test. Если мы наберем команду:

make test ,

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

test <- test.o <- test.c | test.сс | test.f

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

make -p.

Аргументом команды make является цель, которая должна быть достигнута в результате выполнения команды. Если в каталоге имелся файл с программой на языке Фортран, то для достижения цели выполнится последовательность команд:

g77 –c –o test.o test.f
g77 –o test test.o

Трудно, конечно, рассчитывать на то, что команда make корректно сработает без специальных пояснений, если программа состоит из нескольких файлов или требуется подключение каких-либо библиотек. Такие пояснения команда make ищет в файлах makefile или Makefile, причем именно в такой последовательности.

Makefile представляет собой текстовый файл, в котором описаны макроопределения, имеющие вид:

и правила, имеющие вид:

Пример:

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

Синтаксис команды make:

make [-f make-файл] [-p] [-i] [-k] [-s] [-r] [-n] [-b] [-e] [-u] [-t[[целевой_файл ...]]

Команда make обновляет целевой_файл только в том случае, если файлы, от которых он зависит, оказываются новее по времени модификации. Опция -u диктует безусловное обновление.

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

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

Каманда make активно работает с суффиксами имен файлов. Правило создания файла с суффиксом .o из файла с суффиксом .c указывается как раздел с именем .c.o: и пустым списком зависимостей. Команды shell'а, связанные с этим именем, определяют способ получения файла с расширением .o из файла с расширением .c.

Пример:

Здесь описаны правила получения оптимизированных объектных файлов из исходных файлов, имеющих расширение .c.

В заключение в качестве примера рассмотрим Makefile, который использовался для компиляции пакета FDMNES.

----------- раздел макроопределений ------------
PROG = fdmnes - определяем имя выходного файла
OBJ = main.o general.o lecture.o clemf0.o dirac.o \
      coabs.o mat.o sphere.o convolution.o spgroup.o \
      metric.o tab_data.o minim.o fprime.o \
      not_mpi.o tensor.o

определяем переменную OBJ, содержащую список объектных файлов, из которых должна быть собрана программа

FC = ifort - определяем компилятор
FFLAGS = -O - определяем опции компилятора
LDFLAGS = -O - определяем опции линковщика
LIBS = -lmkl_lapack -lmkl -lguide

определяем переменную LIBS, содержащую список библиотек, которые должны подключаться при сборке программы

----------------- раздел целей ---------------------
all: exe - список главных целей
exe: $(OBJ) - определение цели из списка
    ${FС} ${LDFLAGS} -o $(PROG) ${OBJ} ${LIBS} - команда
clean : - ложная цель (удаление объектных файлов)
    rm -f *.o
.f.o : ; $(FС) -c ${FFLAGS} $*.f – правило получения объектных файлов

Назад    Вперед