2.5. Make

2.5.1. Что представляет собой утилита make?

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

    % cc file1.c file2.c

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

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

    % cc file1.o file2.o ... file37.c ...

если мы изменили файл file37.c, а другие файлы не изменяли с прошлой компиляции. Это может значительно ускорить компиляцию, но не решает проблемы с набором команд.

Либо мы можем написать скрипт на языке командного процессора для решения проблемы с набором команд, но при этом будет перекомпилироваться все, что делает этот способ неэффективным для большого проекта.

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

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

Эта программа называется make. Она читает файл, называемый make-файлом, указывающий, как различные файлы зависят друг от друга, и определяет, каким файлам требуются перекомпиляция, а каким нет. Например, правило может говорить нечто вроде "если fromboz.o старее, чем fromboz.c, то это значит, что кто-то изменил fromboz.c, поэтому этот файл нужно перекомпилировать". В make-файле также имеются правила, указывающие программе make, как именно перекомпилировать файл с исходным текстом, делая эту утилиту гораздо более мощным инструментом.

Make-файлы обычно располагаются в том же самом каталоге, что и исходные файлы, к которым они имеют отношение, и могут называться makefile, Makefile или MAKEFILE. Большинство программистов используют имя Makefile, так как при этом оно будет помещено в начало списка файлов каталога, где его легко увидеть. [1]

2.5.2. Пример использования утилиты make

Вот очень простой make-файл:

    foo: foo.c
        cc -o foo foo.c

Он содержит две строки, строку зависимости и строку создания.

Здесь строка зависимости состоит из имени программы (называемой целью (target)), за которой следует двоеточие, затем пробел и имя исходного файла. Когда программа make читает эту строку, она определяет, существует ли foo; если она существует, то сравниваются времена последней модификации foo и foo.c. Если foo не существует или она старее, чем foo.c, то затем смотрится строка создания для определения того, что же нужно предпринять. Другими словами, это правило для определения необходимости перекомпиляции foo.c.

Строка создания начинается с символа табуляции (нажатия клавиши tab) и команды, которую вы должны были бы набрать для создания foo, если бы работали в режиме командной строки. Если foo устарела или не существует, то make выполняет эту команду для ее создания. Другими словами, это правило, которое указывает, как именно перекомпилировать foo.c.

Итак, когда вы вводите команду make, она проверяет, что foo будет соответствовать последним изменениям в foo.c. Этот подход может быть развит в Makefile с сотнями целей--действительно, во FreeBSD возможно полностью перекопилировать операционную систему, просто набрав make world в соответствующем каталоге!

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

    foo: foo.c
        cc -o foo foo.c

    install:
        cp foo /home/me

Мы можем указать программе make, какую цель мы собираемся выполнять, набрав:

    % make target

По этой команде make будет смотреть только на эту цель и игнорировать все другие. Например, если мы наберем make foo с make-файлом выше, то make будет игнорировать цель install.

Если мы просто наберем make без параметров, то make всегда будет искать первую цель и затем остановится без обработки других целей. Так что если в нашем случае мы набрали make, то она перейдет к цели foo, перекомпилирует foo, если это нужно, а затем остановится без перехода к цели install.

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

Это очень сложный для обсуждения предмет. Если вы не очень понимаете, как работает make, лучше всего написать простую программу типа "hello world", создать make-файл, подобный вышеописанному и поэкспериментировать. Затем попробуйте использовать более чем один исходный файл, или исходный файл, включающий include-файл. Здесь будет полезна команда touch--она изменяет дату файла без его редактирования.

2.5.3. Make-файлы FreeBSD

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

    MASTER_SITES=   ftp://freefall.cdrom.com/pub/FreeBSD/LOCAL_PORTS/
    DISTFILES=      scheme-microcode+dist-7.3-freebsd.tgz

    .include <bsd.port.mk>

Теперь, если мы перейдем в каталог с этим портом и наберем команду make, то случится вот что:

  1. Делается проверка на наличие исходного кода этого порта в системе.

  2. Если его нет, осуществляется FTP-соединение к URL, указанному в MASTER_SITES, для сгрузки исходного текста.

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

  4. Делаются все изменения, требуемые для того, чтобы исходный код работал во FreeBSD--этот процесс носит название патчинг (patching).

  5. Выполняются все настройки, требуемые для исходных текстов. (Многие дистрибутивы Unix-программ пытаются определить, на какой версии Unix они компилируются и какие дополнительные возможности Unix имеются--эта информация информация задается в сценариях портов FreeBSD).

  6. Компилируется исходный код программы. В частности, мы меняем каталог на тот, в который были распакованы исходные тексты и выполняем команду make--в собственном make-файле программы имеется необходимая для построения программы информация.

  7. Теперь у нас есть откомпилированная версия программы. Если мы хотим, то можем ее протестировать; когда мы останемся довольными ей, то можем выдать команду make install. При этом программа и все необходимые для работы файлы будут скопированы в правильные каталоги; в базе данных о пакаджах будет сделана запись, чтобы порт мог быть позже с легкостью удален, если он нам не понравится.

Теперь, я думаю, вы согласитесь, что для сценария из четырех строк это выглядит очень впечатляюще!

Секрет заключен в последней строке, которая указывает программе make на включение системного make-файла с именем bsd.port.mk. Легко пропустить эту строчку, но именно в ней содержатся все эти хитрости--кто-то написал make-файл, указывающий программе make на выполнение всех вышеописанных действий (плюс еще многих мною не описанных, включая обработку ошибок, которые могут возникнуть) и любой может получить доступ к нему, просто добавив одну строчку в собственный make-файл!

Если вы хотите взглянуть на эти системынй make-файлы, то они находятся в каталоге /usr/share/mk, но, наверное, лучше сначала набраться практики работы с make-файлами, так как они очень сложны (и если вы будете в них заглядывать, обязательно держите под рукой термос с крепким кофе!)

2.5.4. Продвинутое использование программы make

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

Версия make, поставляемая с FreeBSD, является Berkeley make; руководство находится в файле for it in /usr/share/doc/psd/12.make. Чтобы его просмотреть, выполните

    % zmore paper.ascii.gz

в этом каталоге.

Многие приложения в Коллекции Портов используют GNU make, с которым поставляется хороший набор страниц "info". Если вы устанавливали какие-либо из таких портов, то GNU make будет автоматически установлен под именем gmake. Он также сам доступен в виде порта и пакаджа.

Чтобы просмотреть info-страницы по GNU make, вам придется отредактировать файл dir в каталоге /usr/local/info, добавив для них запись. Это означает добавление примерно такой строки

     * Make: (make).                 The GNU Make utility.

в файл. Как только вы это сделаете, сможете набрать команду info и выбрать пункт make из меню (или в программе Emacs нажать C-h i).

Notes

[1]

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