Искусство программирования для Unix - Эрик Реймонд
Шрифт:
Интервал:
Закладка:
Возникновение make связано с визитом ко мне Стива Джонсона (Steve Johnson — автор yacc и других программ). Когда он пришел, он был очень недоволен тем, что ему пришлось потратить впустую утро, занимаясь отладкой корректной программы (ошибка была устранена, файл не был откомпилирован, и, следовательно, cc *.о не работала). А поскольку я потратил часть предыдущего вечера, справляясь с той же проблемой в разрабатываемом мною проекте, у нас появилась идея создания инструмента для решения данной задачи. Все началось с тщательно продуманной идеи анализатора зависимостей, потом свелось к нечто более простому и в те же выходные превратилось в make. Использование инструментов, которые все еще оставались сырыми, было частью культуры. Make-файлы были текстовыми, а не "волшебно" закодированными бинарными файлами, поскольку это было в духе Unix: печатаемый, отлаживаемый, понятный материал.
Стюарт Фельдман.После ввода команды make, в каталоге проекта программа make просматривает все правила и временные метки, после чего выполняет минимальный объем работы, необходимый для того, чтобы гарантировать актуальность производных файлов.
Хороший пример make-файла умеренной сложности можно взять из исходных кодов программы fetchmail. В дальнейших подразделах он будет рассматриваться снова.
Очень сложные make-файлы (особенно, когда они вызывают вспомогательные make-файлы) могут стать источником осложнений вместо того, чтобы упростить процесс сборки. Ставшее классическим предупреждение впервые прозвучало в статье "Recursive Make Considered Harmful"[131]. Аргумент в данной статье со времени ее публикации в 1997 году стал общепринятым и почти стал переворотом предыдущей практики в сообществе.
Обсуждение утилиты make(1) будет неполным без признания того факта, что она включает в себя одну из худших недоработок конструкции в истории Unix. Использование символов табуляции в качестве необходимых начальных символов командных строк, связанных с правилом, означает, что интерпретация make-файла может радикально измениться из-за невидимых различий в пустых пространствах.
Почему в столбце 1 используется табуляция? Yacc была новой программой, a Lex ещё более новой. Я их еще не попробовал, поэтому предположил, что это было бы хорошим поводом для обучения. После того как я запутался, впервые попробовав Lex, я просто сделал нечто простое с моделью "конец строки-табуляция". Конструкция работала, и поэтому осталась без изменений. А спустя несколько недель сформировалось сообщество пользователей (около десятка человек), причем большинство из них были моими друзьями, и я не хотел им навредить. Остальное, к сожалению, уже стало историей.
Стюарт Фельдман.15.4.2. Утилита make в разработке не на C/C++
Программа make может оказаться полезной не только для программ на C/C++. Языки сценариев, подобные описанным в главе 14, могут не требовать традиционных этапов компиляции и компоновки, однако часто существуют другие виды зависимостей, с которыми поможет справиться утилита make(1).
Предположим, например, что часть кода фактически генерируется из файла спецификации с помощью одной из методик, описанных в главе 9. В данном случае программу make можно использовать для связи файла спецификации и сгенерированного исходного кода. Такой подход гарантирует, что всякий раз при изменении спецификации и повторном выполнении make сгенерированный код будет автоматически обновлен.
Весьма распространенной является практика использования правил make-файлов в целях выражения инструкций для создания документации, так же как и кода. Часто такой подход используется для автоматического создания PostScript или другой производной документации из главных документов, написанных на каком-либо языке разметки (например, HTML или одном из языков создания документов в Unix, которые рассматриваются в главе 18). Фактически такое использование настолько широко распространено, что его стоит проиллюстрировать учебным примером.
15.4.2.1. Учебный пример: использование make для преобразования файла документации
В make-файле программы fetchmail, например, есть три правила, которые связывают файлы FAQ, FEATURES и NOTES с исходными HTML-файлами fetchmail-FAQ.html, fetchmail-features.html и design-notes.html.
HTML-файлы предназначены для просмотра на Web-странице программы fetchmail, но если Web-браузер не используется, то HTML-разметка делает эти файлы неудобными для просмотра. Поэтому FAQ, FEATURES и NOTES представляют собой простые текстовые файлы, предназначенные для быстрого просмотра с помощью редактора или программы-пейджера при чтении собственно исходного кода fetchmail (или, возможно, для размещения на FTP-сайтах, не поддерживающих Web-доступ).
Простые текстовые формы могут быть получены из главных HTML-файлов с помощью распространенной программы lynx(1) с открытым исходным кодом. lynx — является Web-браузером для текстовых дисплеев. Однако если данную программу вызвать с параметром -dump, то она будет достаточно корректно функционировать в качестве преобразователя HTML-ASCII.
Правила make позволяют разработчику редактировать главные HTML-документы, не заботясь впоследствии о повторной ручной сборке простых текстовых форм, поскольку файлы FAQ, FEATURES и NOTES будут соответствующим образом при необходимости каждый раз создаваться заново.
15.4.3. Правила make
Некоторые из наиболее интенсивно используемых правил в типичных make-файлах вообще не выражают зависимостей. Они позволяют связать небольшие процедуры, которые разработчик хочет механизировать, например, создание дистрибутивного пакета или удаление всех объектных файлов для компиляции проекта с нуля.
Нефайловые правила были созданы -умышленно и существовали с первого дня. Правила "make all" и "clean" были моими собственными ранними соглашениями.
Стюарт Фельдман.Существует хорошо развитый набор соглашений о том, какие правила должны присутствовать и как они должны быть названы. Придерживаясь данных соглашений, разработчик создает более понятные и простые в использовании make-файлы.
all
Правило all должно создавать все выполняемые файлы проекта. Обычно в all нет явного критерия. Вместо этого правило ссылается на все цели верхнего уровня в проекте (и, вовсе неслучайно, документирует, каковы они). Традиционно all должно быть первым правилом make-файла, так чтобы оно было единственным правилом, которое выполняется, когда разработчик вводит команду make без аргументов.
test
Запуск автоматизированного тестового пакета для программы, обычно состоящего из набора блочных тестов (unit tests)[132] для поиска регрессий, ошибок или других отклонений от ожидаемого поведения во время процесса разработки. Правило "test" также могут использовать конечные пользователи программы, для того чтобы убедиться, что их инсталляция функционирует корректно.
clean
Удаление всех файлов (таких как бинарные исполняемые и объектные файлы), которые обычно создаются во время выполнения команды make all. Команда make clean должна вернуть процесс сборки программного обеспечения в исходное состояние.
dist
Создание архива исходного кода (обычно с помощью программы tar(1)), который можно распространять как единое целое и использовать для сборки программы заново на другой машине. Целью данной директивы является создание эквивалентного кода, зависящего от all таким образом, чтобы правило make dist автоматически заново собирало целый проект, прежде чем создать его дистрибутивный архив. Это хороший способ избежать ошибок, в результате которых в дистрибутив не включаются действительно необходимые производные файлы (например, простой текстовый файл README в fetchmail, который фактически генерируется из HTML-файла).
distclean
Отбрасывает все, кроме того, что разработчик включил бы в случае упаковки исходного кода с помощью команды make dist. Действие может быть аналогичным команде make clean, но distclean следует в любом случае включать как отдельное правило для документирования происходящего. Если действие отличается, то обычно оно отличается отбрасыванием локальных конфигурационных файлов, которые не являются частью обычной последовательности сборки make all (такой как последовательность, сгенерированная утилитой autoconf(1)autoconf(1) рассматривается в главе 17).