понедельник, 9 мая 2011 г.

make

Это утилита юникс-систем(есть версия линкуса и бсд), которая помагает собирать приложения восновном на языке си. Каталог с исходниками приложенения должен содержать специальный файл(возможные имена: GNUmakefile, Makefile, makefile) в котором описаны связи между отдельными файлами, по которому утилита компилирует исходиники в обьектные файлы, а потом их компонует линковщик.


Обычно простейшая программа на си выглядит примерно так:
main.c
lib.c
defines.h

На последний файл имееют ссылки вида #include два предыдущих.
Чтобы откомпилировать приложение мы пользлуемся утилитой сс:

cc -c main.c
cc -c lib.c
cc -o program main.o lib.o

Сначала мы создали два обьектных файла(исходные тексты переведены в двоизный формат, понятный для процессора). А потом с этих файлов скомпоновали один исполняемый, который и является приложением.

Если приложение состоит из многих файлов, и при этом они сложно взаимосвязанны, а мы сделали изменения в нескольких, то утилита мейк помагает отследить нам эти сложные связи, а также сориентироваться по последним датам правок файлов и перекомпилировать только нужные, а потом скомпоновать обьектные файлы в исполняемый(приложение или библиотеку).

Чтобы утилита заработала, мы должны указать в мейкфайле, что от чего зависимо. На основе этой информации утилита:
  • собирает из этой информации правильную последовательность команд для получения требуемых результирующих файлов;
  • и инициирует создание требуемого файла только в случае, если такого файла не существует, или он старше, чем файлы от которых он зависит.

Вот какой мейкфайл мы должны определить:
program: main.o lib.o
        cc -o program main.o lib.o
main.o lib.o: defines.h

Мейкфайл состоит из правил и переменных. Вот структура правила:
цель1 [цель2 ...]: [реквизит1 реквизит2 ...]
[<TAB>команда1]
    .
    .
    .
[<TAB>командаN]
Цели -- это результирующие файлы, а реквизиты -- это файлы от которых зависят цели. При этом не нужно указывать, что *.o-файлы зависят от *.c-файлов, это мейк знает сам, и знает какую команду нужно выполнять дле решения этой зависимости(cc -c main.c). Поэтому, когда утилита подходит к строке где встречается обьектный файл, первое, что она делает, так этом проверку не являтся ли *.o-файл старше *.c-файла, если так и есть, то сначала перекомпилируется обьетный файл на основании обновленного исходника.

А вот резюме самой комманды:
make [ -f makefile ] [ options ] ... [ targets ] ...

Вот стандартные цели для сборки дистрибутивов GNU:
  • all - выполнить компиляцию пакета(цель поумолчанию)
  • install - установить пакет из дистрибутива(копируются исполняемые файлы, библиотеки и документация в системные директории)
  • uninstall -удалить пакет(удаляются исполнаяемые файлы, библиотеки и документация из системных директорий)
  • clean - удаляются в дистрибутиве обьектные и исполняемые файлы, созданные в процессе компиляции
  • distclean - клин+ удаляются файлы созданные скриптом ./configure(процесс натройки компиляции дистрибутива)

А вот эта цель характерна для BSD:
  • depend - выполняется компиляция/выстраивание зависимостей

А вот тот же наш пример, но при этом в нем используются кроме правил еще и переменные, это нам позволяет в одном месте производить изменения мейкфайла по мере роста исходных файлов приложения:
OBJ = main.o lib.o
program: $(OBJ)
        cc -o program $(OBJ)
$(OBJ): defines.h


Если при запуске make явно не указать цель, то будет обрабатываться первая цель в make-файле, имя которой не начинается с символа «.».

Важный момент мейк-файла это ложные цели Это цели, которые не создают файл, но при этом выполняют, что-то важное. Это например:
all: foo
clean:
  rm -f bar.o bar.c foo.o foo.c
.PHONY: all clean

Метка .PHONY указывает явно, что наши цели "ложные", на случай если в каталоге вдруг окажутся файлы с такими именами, как all и clean.

В обработку мейк-файлов можно сделать более гибкой:
Пускай у нас есть скриптик snozzle, который конвертирует наш специфичный язык(удобный для нас) в язык Си. Мы делаем себе мейк-фал:

%.c: %.snoz
   snozzle $< -o $@
Пояснения предопределенных макросов:
% -- означает один и более символов.
$^ -- означает полный список зависимостей(реквизитов)
$< -- означает первый в списке зависимостей(реквизитов)
$@ -- означает первый в списке целей(имеется ввиду целей и зависимостей ближайших сверху)

В результате:
$ ls
Makefile foo.snoz
$ make
snozzle foo.snoz -o foo.c
cc -c -o foo.o foo.c
echo 'char *builddate="' `date` '"' >bar.c
cc -c -o bar.o bar.c
cc foo.o bar.o -o foo
rm foo.c

А вот применяемые переменные среды ОС в мейк-файлах, их можно определять также внутри мейк-файла. Вот их список:
  • CPPFLAGS command line flags to cpp
  • CFLAGS command line flags to cc
  • CXXFLAGS command line flags to c++
  • LDFLAGS command line flags to ld
  • ASFLAGS command line flags to as
 Чтобы понять причем тут эти утилиты нужно взглянуть на статью Процесс компиляции утилитой gcc внутри

А вот пример мейк-файла приложения написанного в этом контексте:
CXXFLAGS=-g
 
 sim: car.o road.o sim.o event.o
        g++ $(LDFLAGS)  sim.o car.o road.o event.o -lm   -o sim
 
 car.o: car.cc car.h sim.h event.h road.h Makefile
 sim.o: sim.cc sim.h car.h road.h event.h Makefile
 road.o: road.cc road.h sim.h event.h car.h Makefile
 event.o: event.cc event.h sim.h Makefile

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

Отправить комментарий