J445e

## ПРОСТОЕ РУКОВОДСТВО ПО Makefile

Перевод. Оригинал здесь:

  http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/

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

Простой пример

Давайте начнем со списка из следующих трех файлов: hellomake.c, hellofunc.c и hellomake.h, которые представляют:

Типичную основную программу:

<hellomake.c>

  #include <hellomake.h>

  int main() {
    // call a function in another file
    myPrintHelloMake();

    return(0);
  }

EOF

Некоторый функциональный код:

<hellofunc.c>

  #include <stdio.h>
  #include <hellomake.h>

  void myPrintHelloMake(void) {

    printf("Hello makefiles!\n");

    return;
  }

EOF

Включаемый файл заголовка: hellomake.h

<hellomake.h>

  /* example include file */

  void myPrintHelloMake(void);

EOF

Обычно вы должны скомпилировать эту коллекцию кода, выполнив следующую команду:

  gcc -o hellomake hellomake.c hellofunc.c -I.

Это компилирует два файла .c и называет исполняемый файл hellomake. -I. (Include folder) добавлен так, что gcc будет искать включаемый файл hellomake.h в текущем каталоге (.). Без make-файла типичный подход к циклу тестирования -> изменения -> отладки заключается в использовании стрелки вверх в терминале для возврата к последней команде компиляции, чтобы вам не приходилось вводить ее каждый раз, особенно после добавления еще несколько .c файлов в список.

К сожалению, этот подход к компиляции имеет два недостатка. Во-первых, если вы потеряете команду компиляции или перейдете на другой компьютеры, вам придется заново ее вводить, что в лучшем случае неэффективно. Во-вторых, если вы вносите изменения только в один файл .c, перекомпиляция всех их каждый раз также занимает много времени и неэффективна. Итак, пришло время посмотреть, какие возможности нам добавит make-файл.

Самый простой make-файл, который вы можете создать, будет выглядеть примерно так:

<Makefile-1>

  hellomake: hellomake.c hellofunc.c
  <tab>gcc -o hellomake hellomake.c hellofunc.c -I.

EOF

Если вы поместите это правило в файл с именем Makefile или makefile, а затем наберете make в командной строке, оно выполнит команду compile, как вы написали ее в make-файле. Обратите внимание, что

  MAKE БЕЗ АРГУМЕНТОВ ВЫПОЛНЯЕТ ПЕРВОЕ ПРАВИЛО В ФАЙЛЕ.

строка вида

  цель: список файлов

Кроме того, поместив список файлов, из которых цель будет собрана сразу после двоеточия, make будет знает, что правило hellomake должно быть выполнено, если какой-либо из этих файлов изменится. Сразу же вы решили проблему номер 1 и сможете теперь избежать повторного использования стрелки вверх в поисках последней команды компиляции. Однако система все еще неэффективна с точки зрения составления только последних изменений.

ОЧЕНЬ ВАЖНО: перед командой gcc в make-файле стоит знак табуляции (обозначенный в примерах как <tab>). В начале любой команды должен стоять <tab> и make не будет работать, если его там нет.

Чтобы еще немного поднять эффективность процесса построения программы, давайте попробуем следующее:

<Makefile-2>

  CC=gcc
  CFLAGS=-I.

  hellomake: hellomake.o hellofunc.o
  <tab>$(CC) -o hellomake hellomake.o hellofunc.o

EOF

Итак, теперь мы определили некоторые константы CC и CFLAGS. Оказывается, это специальные константы, которые сообщают нам о том, чем и как мы хотим скомпилировать файлы hellomake.c и hellofunc.c. В частности, макрос CC -- это используемый компилятор C, а CFLAGS -- список флагов, которые нужно передать команде компиляции. Поместив объектные файлы -- hellomake.o и hellofunc.o -- в список зависимостей и в правило, make будет знать, что сначала нужно будет отдельно скомпилировать файлы с расширением .c, а затем собрать исполняемый файл hellomake.

Использования этой формы make-файла достаточно для большинства небольших проектов. Однако, здесь отсутствует одна вещь: зависимость от включаемых файлов. Например, если вы внесете изменения в hellomake.h, make не станет перекомпилировать файлы .c, даже если это необходимо. Чтобы это исправить, мы должны указать make, что все файлы .c зависят от определенных файлов .h. Мы можем сделать это, написав простое правило и добавив его в make-файл.

<Makefile-3>

  CC=gcc
  CFLAGS=-I.
  DEPS = hellomake.h

  %.o: %.c $(DEPS)
  <tab>$(CC) -c -o $@ $< $(CFLAGS)

  hellomake: hellomake.o hellofunc.o
  <tab>$(CC) -o hellomake hellomake.o hellofunc.o

EOF

Это дополнение сначала создает макрос DEPS, который представляет собой набор файлов .h, от которых зависят файлы .c. Затем мы определяем правило, которое применяется ко всем файлам, оканчивающимся на суффикс .o. Правило гласит, что файл .o зависит от версии файла .c и файлов .h, включенных в макрос DEPS. Затем правило говорит, что для генерации файла .o необходимо, чтобы make скомпилировал файл .c, используя компилятор, определенный в макросе CC. Флаг -c говорит, что нужно сгенерировать объектный файл, -o $@ говорит, чтобы поместить выходные данные компиляции в файл с именем слева от двоеточия, а $< это первый элемент в списке зависимостей.

[[ Пояснение: специальные переменные $@ и $^, которые являются левой и правой сторонами от : соответственно, делают правило компиляции более общим:

  %: %.c
  <tab>gcc -o "$@" "$<"

конец пояснения ]].

Макрос CFLAGS был определен определен выше.

В качестве последнего упрощения, давайте используем такой макрос:

  OBJ = hellomake.o hellofunc.o

В приведенном ниже примере все включаемые файлы должны быть перечислены как часть макроса DEPS, а все объектные файлы должны быть перечислены как часть макроса OBJ.

<Makefile-4>

  CC=gcc
  CFLAGS=-I.
  DEPS = hellomake.h
  OBJ = hellomake.o hellofunc.o

  %.o: %.c $(DEPS)
  <tab>$(CC) -c -o $@ $< $(CFLAGS)

  hellomake: $(OBJ)
  <tab>$(CC) -o $@ $^ $(CFLAGS)

EOF

А что если мы захотим помещать наши файлы .h в каталог include,  аш исходный код в каталог src, а некоторые локальные библиотеки в каталог lib ? Кроме того, сможем ли мы как-то скрыть эти надоедливые файлы .o, которые повсюду-повсюду? Ответ -- конечно же, да. Следующий make-файл определяет пути к каталогам include и lib и помещает объектные файлы в подкаталог obj в каталоге src. В нем также есть макрос, определенный для любых библиотек, которые вы хотите включить, например, математическая библиотека -lm. Этот make-файл должен находиться в каталоге src. Обратите внимание, что в него также входит правило для очистки ваших исходных и объектных каталогов, если вы наберете make clean. Правило .PHONY не позволит make-программе что-то сделать с файлом с именем clean, на случай, если такой файл существует.

<Makefile-5>

  IDIR=../include
  CC=gcc
  CFLAGS=-I$(IDIR)
  ODIR=obj
  LDIR =../lib
  LIBS=-lm
  _DEPS = hellomake.h
  DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
  _OBJ = hellomake.o hellofunc.o
  OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))

  $(ODIR)/%.o: %.c $(DEPS)
  <tab>$(CC) -c -o $@ $< $(CFLAGS)

  hellomake: $(OBJ)
  <tab>$(CC) -o $@ $^ $(CFLAGS) $(LIBS)

  .PHONY: clean
  clean:
  <tab>rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~

EOF

Так что теперь у вас есть очень хороший make-файл, который вы сможете изменять для управления небольшими и средними программными проектами. Вы можете добавить несколько правил в make-файл; Вы даже можете создавать правила, которые вызывают другие правила. Для получения дополнительной информации о make-файлах и функции make обратитесь к "GNU Make Manual"

  http://www.gnu.org/software/make/manual/make.html ,

которое расскажет вам больше, чем вы когда-либо хотели (really).

ССЫЛКИ

1. A Simple Makefile Tutorial

  http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/

2. Practical Makefiles, by example

  http://nuclear.mutantstargoat.com/articles/make/

3. Тихомиров В.П., Давидов М.И.

   Операционная система ДЕМОС. Инструментальные средства программиста
   (1988, Финансы и статистика)
