вторник, 16 октября 2007 г.

Шпаргалка по созданию Makefile

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

Например:

Определить значение перенной можно при помощи дериктив define... endef:

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

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

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

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

В именах файлов могут использоваться wildcards:

objects = *.o

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

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

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

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

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

Больше почитать о Makefile можно здесь: Writing Makefiles

Об опциях компилятора можно почитать здесь.

4 комментария:

Анонимный комментирует...

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

1. naverno .cpp:.o ?
2. t.e. compilit (-c) obektnie file?

sash_ko комментирует...

>> 1. naverno .cpp:.o ?
Нет, именно .cpp.o:
Это называется суффиксные правила. Пусть есть make-файл:
#1
mytest: lib.o map.o vector.o
#2
    gcc $^ -o $@
#3
.cpp.o:
#4
    gcc -c $^

#1 означает, что для того, что бы получить mytest, нужно иметь 3 объектных файла (условие, для нормального выполнения комманды #2). Если эти файлы еще не созданны, make ищет, нет ли указания, как их создать. Если явного указания нету (когда объектный файл указан как цель; явное указание более приоритетное, чем суффиксное), то make считает, что объектный файл пожно получить из .c или .cpp файла с таким же именем (lib.o из lib.cpp). #3 показывает, как получить .о из .срр (если используетяс асм, то суффиксное правило будет .s.o и т.д.). По сути, если комманда простая, типа #4, то ее вообще можно не писать, make умный, он сам догадается. Но если необходимо указать, например, что .cpp файлы лежат в какой-то особенно дирректории, то можно создать правило #3.

>> 2. t.e. compilit (-c) obektnie file?

Да, объектные файлы, которые нужны для создания mytest. Выполняется примерно так:
1. Проверяются зависимости #1.
2. Если зависимости не созданы, проверяется #3.
3. Выполняется #4 (создаются объектные файлы).
4. Выполняется #2 (создается mytest).

Ростислав комментирует...

Добрый день!
имеется Makefile:
____
CMD = cmdA cmdB cmdC

all: msg compile

msg:
@echo "Test msg"

compile:
@for X in $(CMD); do echo "$$X"; done

cmdA: override CMD = cmdA
cmdA: compile

cmdB: override CMD = cmdB
cmdB: compile

cmdC: override CMD = cmdC
cmdC: compile
____

вопрос - как заставить выполниться следующее: "make cmdB cmdA" ("make" - работает и "make cmd" - тоже)?

Олег комментирует...
Этот комментарий был удален автором.