Программирование PC Руководство GNU make: главы 13-16 Sat, February 22 2025  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


Руководство GNU make: главы 13-16 Печать
Добавил(а) microsin   

Продолжение перевода руководства GNU make [1].

Примечание: объяснение некоторых специфических терминов make/makefile см. в разделе "Словарик Makefile", в конце статьи [2].

[13. Интеграция GNU make]

GNU make часто используется как один из компонентов большого набора инструментов, включая интегрированные среды разработки (integrated development environments, IDE), тулчейны компилятора, и другие утилиты. Роль make заключается в запуске команд и анализе их успешного завершения: для этого не требуется специальная интеграция. Однако иногда удобно сильнее привязывать make к другим частям системы как на более высоком уровне (на уровне инструментов, запускающих make), так и на более низком уровне (инструменты, которые запускает make).

13.1. Совместное использование слотов заданий с GNU make

У GNU make есть возможность запустить несколько рецептов одновременно (см. 5.4. Параллельное выполнение), и ограничивать общее количество параллельных заданий (jobs) даже в рекурсивных вызовах make (см. Communicating Options to a Sub-make). Инструменты, которые вовлекают make, сами могут запускать несколько операций параллельно, либо используя несколько потоков (threads) либо несколько процессов (processes), могут быть улучшены для участия в системе управления заданиями GNU make, чтобы гарантировать, что общее количество активных потоков/процессов, запущенных на системе, не превышало максимального количества слотов, предоставленного для GNU make.

GNU make использует метод, так называемый "jobserver" для управления количеством активных заданий по всем запущенным рекурсиям. Реальная реализация jobserver варьируется между различными операционными системами, но некоторые фундаментальные аспекты всегда верны.

Во-первых, make предоставит информацию, необходимую для досутпа к jobserver через окружение к своим дочерним процессам, в переменной окружения MAKEFLAGS. Инструменты, которые хотят участвовать в протоколе jobserver, должны будут проанализировать эту переменную и найти в ней слово, начинающееся на --jobserver-auth=. Значение этой опции будет взаимодействие с jobserver. Интерпретация этого значения описывается в секциях далее.

Учтите, что переменная MAKEFLAGS может содержать несколько экземпляров опции --jobserver-auth=. Релевантен только последний из них.

Во-вторых, каждая запускаемая команда make имеет один неявный job-слот, зарезервированный для неё до запуска. Любая инструментальная утилита, которая хочет участвовать в протоколе jobserver, должна предполагать, что она всегда может выполнять одно задание без необходимости вообще обращаться к jobserver.

И наконец, крайне важно, чтобы инструменты, участвующие в протоколе jobserver, возвращали точное количество слотов, полученных от jobserver, обратно в jobserver перед выходом, даже при наличии ошибок. Помните, что неявный job-слот не должен возвращаться на jobserver! Возврат слишком малого (неправильного) количества слотов означает, что эти слоты будут потеряны для остального процесса сборки; возврат слишком большого (неправильного) количества слотов означает, что будут свободны дополнительные слоты. Команда make верхнего уровня напечатает сообщение об ошибке в конце сборки, если обнаружит некорректное количество слотов, доступное в jobserver.

В качестве примера предположим, что вы реализуете линкер, который обеспечивает многопоточное функционирование. Вы хотели бы улучшить линкер так, чтобы если он вызывается GNU make, то он мог участвовать в протоколе jobserver, чтобы управлять тем, сколько потоков можно было бы использовать во время линковки. Сначала необходимо модифицировать линкер, чтобы определить, установлена ли переменная окружения MAKEFLAGS. Затем вам нужно пропарсить переменную, чтобы определить, доступен ли jobserver, и как к нему обратиться. Если он доступен, то вы можете обратиться к нему, чтобы получить информацию о слотах заданий (job slot) управляющую тем, сколько параллелизма может занять ваш линкер. После этого ваш линкер должен возвратить в jobserver обратно те слоты заданий, которые были им заняты.

13.1.1. Интеграция POSIX Jobserver. На POSIX-системах jobserver реализован одним из двух способов: на системах, которые это поддерживают, GNU make создает именованный канал (named pipe), и использует его для jobserver. В этом случае опция аутентификации будет иметь форму --jobserver-auth=fifo:PATH, где 'PATH' имя пути для named pipe. Чтобы обратиться к jobserver, вы должны открыт путь named pipe и выполнить чтение/запись, как описано ниже.

Если система не поддерживает именованные каналы, или если пользователь предоставил опцию --jobserver-style и указал 'pipe', то jobserver будет реализован как простой канал UNIX (simple UNIX pipe). В этом случае опция аутентификации будет иметь форму --jobserver-auth=R,W, где 'R' и 'W' это неотрицательные целые числа, представляющие дескрипторы файла: 'R' для чтения (read file descriptor) и 'W' для записи (write file descriptor). Если любой или оба эти дескриптора отрицательные, то это означает, что jobserver запрещен для этих процессов.

Когда используется simple pipe, к jobserver будут иметь доступ только строки команд, описывающие рекурсивные вызовы make (см. 5.7.1. Как работает переменная MAKE). Когда вы пишете файлы makefile, необходимо убедиться, что команда помечена как рекурсивная (чаще всего с помощью префикса в командной строке маркером +, см. 5.7. Рекурсивное использование make). Обратите внимание, что сторона чтения канала jobserver установлена в режим "blocking". Это не должно быть изменено.

В обоих реализациях jobserver канал pipe будет предварительно загружен односимвольным токеном (single-character token) для каждого доступного задания (job). Чтобы получить дополнительный слот задания, вы должны прочитать один символ из jobserver; чтобы освободить слот, вы должны записать один символ обратно в jobserver.

Важный момент: когда вы освобождаете слот задания, то записывайте тот же самый символ, который был вами прочитан. Не следует полагать, что все токены содержат одинаковые символы; различные символы могут иметь для GNU make разный смысл. Порядок чтения и записи не имеет значения, поскольку make все равно не знает, в каком порядке будут выполняться задания.

Для обеспечения надежности внедрения необходимо учитывать различные состояния ошибок:

• Если у вас есть аргумент командной строки, управляющий многопоточностью вашей инструментальной утилиты, то определите, должен ли ваш инструмент обнаруживать ситуации, в которых указаны и сервер заданий, и аргумент командной строки, и как инструмент на это должен реагировать.
• Если ваша утилита не распознает формат строки --jobserver-auth, то она должна предполагать, что jobserver использует другой стиль, и не может подключиться.
• Если ваш инструмент определил, что опция --jobserver-auth ссылается на simple pipe, но указанные дескрипторы файлов закрыты, то это означает, что вызывающий процесс make не считает ваш инструмент рекурсивным вызовом make (например, командная строка не имела префикса из символа +). Вы должны оповестить пользователей об этой ситуации.
• Ваш инструмент должен обеспечить обратную запись токенов даже в условиях возникновения ошибки. Это включает не только ошибки в вашем инструменте, но также и внешние факторы, такие как прерывания (сигнал SIGINT), и т. п. Вы можете захотеть установить обработчики сигнала, чтобы обслужить эти обратные записи токенов.
• Ваш инструмент может также проверить первое слово переменной MAKEFLAGS, чтобы обнаружить в нем символ n. Если этот символ присутствует, то make была запущена с опцией '-n', и ваш инструмент может захотеть остановиться без выполнения каких-либо операций.

13.1.2. Интеграция Windows Jobserver. На Windows-системах jobserver реализован как именованный семафор (named semaphore). Этот семафор будет установлен начальным количеством доступных слотов заданий; чтобы получить слот, вы должны ждать освобождения семафора (или с таймаутом, или без таймаута). Для освобождения слота освободите семафор.

Чтобы получить доступ к семафору, вы должны пропарсить переменную MAKEFLAGS, и найти строку аргумента --jobserver-auth=NAME, где 'NAME' это имя именованного семафора. Используйте это имя вместе с OpenSemaphore, чтобы создать дескриптор (handle) для семафора.

Для --jobserver-style единственный допустимый стиль 'sem'.

Для обеспечения надежности внедрения необходимо учитывать различные состояния ошибок:

• Обычно у вас должен быть аргумент командной строки, управляющий многопоточностью вашего инструмента. Подумайте о том, должен ли ваш инструмент обнаруживать ситуации, в которых указан и сервер заданий, и аргумент командной строки, и как инструмент должен на это реагировать.
• Ваш инструмент должен обеспечивать освобождение семафора столько раз, сколько он был взят, даже в состояниях ошибок. Это включает не только ошибки самого инструмента, но также и внешние факторы, такие как прерывания (сигнал SIGINT), и т. п. Вы можете захотеть установить обработчики сигнала, чтобы обслужить эти обратные освобождения семафора.

13.2. Синхронизированный вывод в терминал

Обычно GNU make будет запускать все команды с доступом к одним и тем же потокам стандартного вывода (stdout) и вывода ошибок (stderr), с какими была сама запущена make. Ряд инструментов определяет, является вывод терминалом, или нет, и используют эту информацию для изменения стиля вывода. Например, если вывод происходит в терминал, то инструмент может добавить в вывод символы управления цветом, или даже поменять место курсора на экране. Если вывод происходит не в терминал (а например в файл), то эти специальные управдяющие символы не выдаются, что не повредит файлы лога и т. п.

Опция --output-sync (см. 5.4.2. Вывод при параллельном выполнении) отключит обнаружение терминала. Когда синхронизация вывода разрешена, GNU make записывает вывод всех команд в файл, так что его их вывод может быть записан как блок без влияния на вывод других команд. Это значит, что все утилиты, запускаемые make, будут полагать, что их вывод не будет отображаться в терминале, даже когда это так (поскольку make отобразит вывод запущенной параллельно команды после её завершения).

Чтобы облегчить жизнь инструментам, который хотели бы определить, будет ли их вывод отображаться на терминале, GNU make установит переменные окружения MAKE_TERMOUT и MAKE_TERMERR перед запуском любой команды. Инструменты, которые хотели бы определить, будет ли отображаться стандартный вывод или вывод ошибок отображаться в терминале, могут проверить эти переменные окружения, существуют ли они, и содержат ли непустое значение. Если это так, что инструмент может предполагать, что вывод будет (в конечном итоге) показан на терминале. Если эти переменные не установлены, или имеют пустое значение, то инструмент вернется обратно к своим нормальным методам определения, происходит ли вывод в терминал, или нет.

Содержимое переменных может быть проанализировано для определения типа терминала, который будет использоваться для отображения вывода.

Подобным образом среды, которые вызывают make и хотели бы захватить вывод и в конечно итоге отобразить его на терминале (или на некоторых дисплеях, которые могут интерпретировать символы управления терминалом), могут установить эти перемененные перед запуском утилиты make. GNU make не будет модифицировать эти переменные окружения, если при своем старте обнаружит их существование.

[14. Функциональные возможности GNU make]

Здесь приведено суммарное описание фич GNU make для сравнения и учета других версий make. В качестве базовых рассматриваются фичи make в BSD-системах 4.2. Если вы заинтересованы в написании переносимых файлов makefile, то не должны использовать перечисленные здесь фичи make, или те, что перечислены в главе 15. Несовместимые и отсутствующие фичи.

Многие фичи были привнесены из версии make в System V.

• Переменная VPATH и её специальный смысл. См. 4.5. Директории поиска для prerequisites. Эта фича существует в System V make, но не документирована. Она документирована в в 4.3 BSD make (который говорит, что имитирует VPATH-фичу System V).
• Подключаемые файлы makefile. См. 3.3. Подключение других файлов Makefile. Возможность подключения нескольких файлов одной директивой это расширение GNU.
• Переменные считываются из окружения и передаются через него. См. 6.10. Переменные из окружения.
• Опции, переданные через переменную MAKEFLAGS в рекурсивные вызовы make. См. 5.7.3. Передача опций в sub-make.
• Автоматическая переменная $% устанавливается в имя элемента при ссылке на архив. См. 10.5.3. Автоматические переменные make.
• Автоматические переменные $@, $*, $%, $? и $< имеют соответствующие формы наподобие $(@F) и $(@D). Разработчики make обобщили это до $^ как очевидное расширение. См. 10.5.3. Автоматические переменные make.
• Ссылки на переменные подстановки. См. 6.1. Ссылки на переменные: общая информация.
• Опции командной строки '-b' и '-m' принимаются и игнорируются. В System V make эти опции реально что-то делают.
• Выполнение рекурсивных команд для запуска make через переменную MAKE, даже если указаны опции '-n', '-q' или '-t'. См. 5.7. Рекурсивное использование make.
• Поддержка суффикса '.a' в правилах суффикса. См. 11.4. Правила суффикса для файлов архива. Эта фича устаревшая в GNU make, потому что достаточно использовать существующую общую фичу цепочки правил (см. 10.4. Цепочки неявных правил), которая позволяет применять одно правило шаблона для установки элементов в архиве (см. 11.2. Неявное правило для целевых объектов элемента архива).
• Расположение строк и комбинаций backslash/newline в рецептах сохраняется при печати рецептов, так что они появляются так же, как и в файле makefile, за исключением удаления начального пробела.

Следующие фичи были инспирированы различными другими версиями make. В некоторых случаях непонятно, какая версия на какую оказывала влияние.

• Правила шаблона, использующие '%'. Это было реализовано в нескольких версиях make. Нет уверенности, кто изобрел это первым, но эта фича довольно распространена. См. 10.5. Определение и переопределение правил шаблона.
• Цепочка правил и неявные промежуточные файлы. Это реализовал Stu Feldman в своей версии make для AT&T Eighth Edition Research Unix, и позднее Andrew Hume из AT&T Bell Labs в своей программе mk (где это он называет транзитивным замыканием, "transitive closure"). Разработчики GNU make не уверены, было ли это взято у них, или же это было придумано независимо и одновременно. См. 10.4. Цепочки неявных правил.
• Автоматическая переменная $^, содержащая список всех prerequisites текущего целевого объекта. Разработчики GNU make это не выдумывали, и не имеют понятия, кто это сделал. См. 10.5.3. Автоматические переменные make. Автоматическая переменная $+ это простое расширение $^.
• Флаг "что если" ('-W' в GNU make) изобрел (насколько известно) Andrew Hume в mk. См. 9.3. Вместо выполнения recipes.
• Концепция одновременного выполнения нескольких действий (параллелизм) существует во многих инкарнациях make и подобных программ, но не в реализациях System V или BSD. См. 5.3. Выполнение рецепта.
• Ряд различных инструментов сборки, поддерживающих параллелизм, также поддерживают сборку вывода и его отображение одним блоком. См. 5.4.2. Вывод при параллельном выполнении.
• Измененные ссылки на переменные с использованием подстановки шаблонов поступили из SunOS 4. См. 6.1. Ссылки на переменные: общая информация. Этот функционал был предоставлен в GNU make функцией patsubst до внедрения альтернативного синтаксиса для совместимости с SunOS 4. Не совсем понятно, кто кого вдохновлял, поскольку GNU make patsubst существовала до выхода релиза SunOS 4.
• Специальное значение символов '+', вставленных в начало строк рецепта (см. 9.3. Вместо выполнения recipes), предписано стандартом IEEE 1003.2-1992 (POSIX.2).
• Синтаксис '+=' для добавления к значению переменной пришел из SunOS 4 make. См. 6.6. Добавление текста к переменным.
• Синтаксис 'archive(mem1 mem2...)' для списка нескольких элементов в одном файле архива пришел из SunOS 4 make. См. 11.1. Элементы архива в качестве целевых объектов.
• Директива -include для подключения нескольких файлов makefile без выдачи ошибки для не существующего файла пришла из SunOS 4 make (однако следует обратить внимание, что SunOS 4 make не позволяет указать несколько файлов makefile в одной директиве -include). Такая же фича с именем sinclude появилась в SGI make и возможно в других make.
• Оператор присваивания != шелла существует во многих BSD make и специально реализован в GNU make, чтобы было аналогичным поведение этих реализаций.
• Различные инструменты поддержки сборки реализованы с использованием языков скриптов, таких как Perl или Python, обеспечивая таким образом естественный язык сценариев, аналогичный интеграции GNU make и GNU Guile.

Остальные фичи это новые изобретения, которые появились в GNU make:

• Использование опции '-v' или '--version' для печати версии и информации копирайта.
• Использование опции '-h' или '--help' для вывода общей подсказки по опциям make.
• Просто расширяемые (simply-expanded) переменные. См. 6.2. Две разновидности (flavor) переменных.
• Автоматическая передача присваивания переменных командной строки через переменную MAKE для рекурсивных запусков make. См. 5.7. Рекурсивное использование make.
• Использование опции '-C' или '--directory' командной строки для смены директории. См. 9.8. Сводка по опциям make.
• Создание буквальных определений переменной с помощью define. См. 6.8. Определение многострочных переменных.
• Декларирование фальшивых целевых объектов (phony targets) с помощью специального целевого объекта .PHONY. Andrew Hume из AT&T Bell Labs реализовал подобную фичу в своей программе mka с помощью другого синтаксиса. Это похоже на одновременное изобретение. См. 4.6. Фальшивые цели (Phony Targets).
• Манипуляция текстом с помощью вызываемых функций. См. 8. Функции для преобразования текста.
• Использование опции '-o' или '--old-file' для возможности сделать вид, что время модификации файла старое. См. 9.4. Как избежать перекомпиляции некоторых файлов.
• Операторы ветвления (проверка условий). Эа фича была реализована множество раз в различных версиях make; это выглядит как естественное расширение, позаимствованное из препроцессора C и подобных языков макросов, и не является революционной концепцией. См. 7. Операции проверки условий в файлах Makefile.
• Указание пути для поиска подключаемых файлов makefile. См. 3.3. Подключение других файлов Makefile.
• Указание дополнительных файлов makefile для чтения с помощью переменной окружения. См. 3.4. Переменная MAKEFILES.
• Вырезание начальных последовательностей './' из имен файлов, так что ./file и file считаются одним и тем же файлом.
• Использование специального метода поиска для библиотечных prerequisites, записанных в форме '-lname'. См. 4.5.6. Поиск по директориям для линковки библиотек.
• Разрешено суффиксам для правил суффикса содержать любые символы (см. 10.7. Старомодные правила суффиксов). В других версиях make они должны начинаться с '.', и не должны содержать любые символы '/'.
• Отслеживается текущий уровень рекурсии с помощью переменной MAKELEVEL. См. 5.7. Рекурсивное использование make.
• Предоставляются любые goals, указанные через командную строку, в переменной MAKECMDGOALS. См. 9.2. Аргументы, указывающие goals.
• Указываются правила статического шаблона. См. 4.12. Правила статического шаблона.
• Предоставляется селективный поиск vpath. См. 4.5. Директории поиска для prerequisites.
• Предоставляются ссылки на вычисляемые переменные. См. 6.1. Ссылки на переменные: общая информация.
• Обновление файлов makefile. См. 3.5. Как заново создаются файлы Makefile. System V make имеет весьма ограниченную форму подобного функционала в том смысле, что она будет проверять файлы SCCS для файлов makefile.
• Различные новые встроенные неявные правила. См. 10.2. Каталог встроенных правил.
• Загрузка динамических объектов, которые могут модифицировать поведение make. См. 12.2. Загрузка динамических объектов.

[15. Несовместимые и отсутствующие фичи]

Программы make в различных других системах поддерживают некоторые фичи, которые не реализованы в GNU make. Стандарт POSIX.2 (IEEE Standard 1003.2-1992), который описывает поведение make, не требует наличия поддержки этих функций.

• Целевой объект формы 'file((entry))' обозначает элемент файла архива file. Элемент выбирается не по имени, а по тому, что он является объектным файлом, который определяет запись элемента символа линкера. Эта фича не была введена в GNU из-за отсутствия модульности размещения знаний в make о внутреннем формате таблиц символов архивного файла. См. 11.2.1. Обновление директорий символов архива.

• Суффиксы (используемые в правилах суффикса), которые заканчиваются на символ '~' имеют для System V make специальный смысл; они ссылаются на файл SCCS, соответствующий файлу, который можно получить без '~'. Например, правило суффикса '.c~.o' сделало бы файл n.o из файла SCCS s.n.c. Для полного покрытия требуется целая серия таких правил суффиксов. См. 10.7. Старомодные правила суффиксов. В GNU make все эти серии случаев обрабатываются двумя правилами шаблона для извлечения из SCCS, в комбинации с общей фичей цепочек правил. См. 10.4. Цепочки неявных правил.

• В System V и 4.3 BSD make, найденные файлы путем поиска по VPATH (см. 4.5. Директории поиска для prerequisites), имеют свои имена измененными внутри рецептов. Разработчики GNI make посчитали, что гораздо яснее всегда использовать автоматические переменные, так что для make эта фича становится ненужно.

• В некоторых реализациях Unix make автоматическая переменная $*, появляющаяся в prerequisites правила, имеет удивительно странную "особенность" расширения до полного имени целевого объекта этого правила. Невозможно себе представить, что происходило в сознании разработчиков Unix make, чтобы сделать подобное; это совершенно не соответствует обычному определению $*.

• В некоторых реализациях Unix make поиск неявного правила (см. 10. Использование неявных правил), по-видимому, выполняется для ВСЕХ целевых объектов, а не только для тех, у которых нет рецептов. Это означает, что вы можете сделать так:

foo.o:
        cc -c foo.c

.. и Unix make проявит интуицию, что foo.o зависит от foo.c.

Разработчики GNU make считают такое использование нарушением. Свойства prerequisite для make четко определены (для GNU make, по крайней мере), и выполнение такой вещи просто не соответствует модели.

• GNU make не подключает любые встроенные неявные правила для компиляции или препроцессинга программ EFL. Если разработчики услышат, что кто-то использует EFL, то это с радостью будет добавлено.

• Похоже, что в SVR4 make правило суффикса может быть указано без рецепта, и это обрабатывается так, как если бы присутствовал пустой рецепт (см. 5.9. Использование пустых рецептов). Например:

.c.a:

.. переназначит встроенное правило суффикса .c.a.

Разработчики GNU make считают, что для правила без рецепта яснее будет всегда просто добавлять список prerequisite для целевого объекта. Пример выше можно легко переписать, чтобы получить желаемое поведение GNU make:

.c.a: ;

• Некоторые версии make запускают шелл с флагом '-e' за исключением использования '-k' (см. 9.6. Тестирование компиляции программы). Флаг '-e' говорит шеллу выполнить выход, как только любая запущенная программа вернет ненулевой статус. Разработчики GNU make считают, что понятнее писать каждую строку рецепта так, чтобы она была самодостаточной и не требовала такого особого обращения.

[16. Соглашения Makefile]

Здесь описываются общепринятые соглашения для написания файлов Makefile для программ GNU. Использование Automake поможет вам писать Makefile, который следует этим соглашениям. Для дополнительной информации по переносимым Makefile см. стандарт POSIX и документацию Autoconf [11].

16.1. Общие соглашения для файлов Makefile

Каждый Makefile должен содержать такую строку:

SHELL = /bin/sh

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

Различные программы make имеют несовместимые списки суффиксов и неявные правила, и это иногда создает путаницу или неправильное поведение. Поэтому рекомендуется явно установить список суффиксов, используя только те суффиксы, которые действительно вам нужны в конкретном Makefile, примерно так:

.SUFFIXES:
.SUFFIXES: .c .o

Первая строка очистит список суффиксов, а вторая вводит все суффиксы, которые могут быть субъектом для неявного правила в этом Makefile.

Не подразумевается, что . находится в пути для выполнения команды. Когда вам нужно запустить программы, которые являются частью вашего пакета во время создания, пожалуйста убедитесь, что используете ./, если программа собирается как часть make или $(srcdir)/, если файл является частью исходного кода. Без этих префиксов используется текущий путь поиска.

Различие между ./ (директория сборки) и $(srcdir)/ (директория исходников) важно, потому что пользователи могут выполнять сборку в отдельной директории, используя опцию '--srcdir' для configure. Правило в форме:

foo.1 : foo.man sedscript
        sed -f sedscript foo.man > foo.1

.. потерпит неудачу, когда директория сборки не совпадает с директорией исходного кода, потому что foo.man и sedscript находятся в директории исходного кода.

При использовании GNU make полагаться на 'VPATH' для поиска исходного файла будет работать в случае, когда есть один файл зависимости, поскольку автоматическая переменная '$< ' будет представлять исходный файл, где бы он ни находился (многие версии make установят '$< ' только в неявных правилах). Целевой объект Makefile наподобие

foo.o : bar.c $(CC) -I. -I$(srcdir) $(CFLAGS) -c bar.c -o foo.o

.. должен быть вместо этого переписан:

foo.o : bar.c
        $(CC) -I. -I$(srcdir) $(CFLAGS) -c $< -o $@

.. чтобы 'VPATH' работала корректно. Когда у целевого объекта несколько зависимостей, использование явного '$(srcdir)' будет самым простым способом заставить правило работать хорошо. Например, целевой объект, показанный выше, для foo.1 лучше написать так:

foo.1 : foo.man sedscript
        sed -f $(srcdir)/sedscript $(srcdir)/foo.man > $@

Дистрибутивы GNU обычно содержат некоторые файлы, которые не являются исходными файлами - например, файлы Info, результат вывода из Autoconf, Automake, Bison или Flex. Поскольку эти файлы обычно находятся в исходной директории, они всегда должны появляться в исходной директории, а не в директории сборки. Так что правила Makefile для их обновления должны помещать обновленные файлы в исходную директорию.

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

Попробуйте сделать целевые объекты сборки и инсталляции, чтобы они по крайней мере (и все их подчиненные целевые объекты) работали правильно с многопоточным запуском make.

16.2. Утилиты в файлах Makefile

Пишите команды Makefile (и любые скрипты шелла, такие как configure) для запуска по управлением оболочки sh (это стандартный Bourne shell и POSIX shell), а не csh. Не используйте никаких специальных фич ksh или bash, или фич POSIX, которые не широко поддерживаются в традиционном Bourne sh.

Скрипт configure и правила Makefile для сборки и инсталляции не должны использовать любые утилиты, кроме этих:

awk cat cmp cp diff echo expr false grep install-info ln ls
mkdir mv printf pwd rm rmdir sed sleep sort tar test touch tr true

Программы компрессии, такие как gzip, могут использоваться в правиле dist.

Придерживайтесь как правило широко поддерживаемых (обычно указанных POSIX) опций и фич этих программ. Например, не используйте 'mkdir -p', как это может быть удобно, потому что некоторые системы не поддерживают это вообще, и с другими утилитами это может быть небезопасно для параллельного выполнения. Для списка известных несовместимостей, см. "Portable Shell Programming" в документации Autoconf [11].

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

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

ar bison cc flex install ld ldconfig lex
make makeinfo ranlib texi2dvi yacc

Используйте для них следующие переменные make:

$(AR) $(BISON) $(CC) $(FLEX) $(INSTALL) $(LD) $(LDCONFIG) $(LEX)
$(MAKE) $(MAKEINFO) $(RANLIB) $(TEXI2DVI) $(YACC)

Когда вы используете ranlib или ldconfig, следует убедиться, что ничего плохого не происходит, если в системе нет рассматриваемой программы. Организуйте игнорирование ошибки от этой команды и распечатайте сообщение перед командой, чтобы сообщить пользователю, что сбой этой команды не означает проблему (в этом может помочь Autoconf макрос 'AC_PROG_RANLIB').

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

Дополнительные утилиты, которое можно использовать через переменные Make:

chgrp chmod chown mknod

Допустимо использовать и другие утилиты в разных частях Makefile (или скриптах), предназначенные только для определенных систем, где вы знаете, что эти утилиты существуют.

16.3. Переменные для указания команд

Файлы Makefile должны предоставлять переменные для переназначения определенных команд, опций и т. д.

В частности, вы должны запускать большинство программных утилит через переменные. Таким образом, если используете Bison, и у вас есть переменная с именем BISON, у которой значение установлено через 'BISON = bison', то ссылайтесь на эту программу через $(BISON) всякий раз, когда используете Bison.

Утилиты управления файлами, такие как ln, rm, mv и так далее, не нуждаются в обращении по ссылкам через переменные, поскольку нет необходимости заменять их другими программами.

Каждая переменная имени программы должна использоваться с переменой опций, используемой для предоставления опций программе. Добавьте 'FLAGS' к имени переменной программы, чтобы создать имя переменной для опций программы, например BISONFLAGS (используются общепринятые имена CFLAGS для флагов компилятора C, YFLAGS для yacc и LFLAGS для lex, которые составляют исключения для этого правила, но они сохранены, потому что стали стандартными). Используйте CPPFLAGS в любой команде компиляции, которая запускает препроцессор, и LDFLAGS в как любой команде компиляции, которая делает линковку, так и любом непосредственном использовании линкера ld.

Если есть опции компилятора C, которые должны использоваться для правильной компиляции определенных файлов, то не включайте их в CFLAGS. Пользователи ожидают, что сами свободно смогут указывать CFLAGS. Вместо этого организуйте передачу необходимых опций компилятору C независимо от CFLAGS, путем их явного написания в командах компиляции, или путем определения неявного правила, примерно так:

CFLAGS = -g
ALL_CFLAGS = -I. $(CFLAGS)
.c.o:
        $(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $<

Подключите опцию '-g' в CFLAGS, потому что это не требуется для правильной компиляции. Вы можете считать это значением по умолчанию, которое только рекомендуется. Если пакет настроен так, что он компилируется по умолчанию с помощью GCC, то вы также можете подключить опцию '-O' в значение по умолчанию CFLAGS.

Поместите CFLAGS последней в команду компиляции, после других переменных, содержащих опции компилятора, так пользователь может использовать CFLAGS для переназначения других опций.

CFLAGS должна использоваться для каждой команды вовлечения компилятора C, как тех команд, которые делают компиляцию, так и для тех, которые делают линковку.

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

Каждый Makefile должен также определять переменные INSTALL_PROGRAM и INSTALL_DATA (по умолчанию для INSTALL_PROGRAM должно быть задано $(INSTALL); умолчание для INSTALL_DATA должно быть ${INSTALL} -m 644). Тогда это должно использоваться для команд фактической инсталляции, для исполняемых файлов и не исполняемых файлов соответственно. Минимальное использование этих переменных происходит следующим образом:

$(INSTALL_PROGRAM) foo $(bindir)/foo
$(INSTALL_DATA) libfoo.a $(libdir)/libfoo.a

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

Допустимо, но не обязательно, устанавливать несколько файлов в одной команде, в которой последний аргумент это директория:

$(INSTALL_PROGRAM) foo bar baz $(bindir)

16.4. DESTDIR: поддержка поэтапных инсталляций

DESTDIR это переменная, предшествующая каждому инсталлируемому целевому файлу, примерно так:

$(INSTALL_PROGRAM) foo $(DESTDIR)$(bindir)/foo
$(INSTALL_DATA) libfoo.a $(DESTDIR)$(libdir)/libfoo.a

Переменная DESTDIR указывается пользователем в командной строке make как абсолютное имя файла. Например:

make DESTDIR=/tmp/stage install

DESTDIR должна поддерживаться целевыми объектами install* и uninstall*, поскольку это единственные целевые объекты, для которых это полезно.

Если ваш шаг инсталляции обычно устанавливает /usr/local/bin/foo и /usr/local/lib/libfoo.a, то инсталляция, вовлекаемая примером выше, будет устанавливать вместо этого /tmp/stage/usr/local/bin/foo и /tmp/stage/usr/local/lib/libfoo.a.

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

Вы вообще не должны устанавливать значение DESTDIR в своем Makefile; тогда файлы по умолчанию установятся в ожидаемое место положение. Также указание DESTDIR не должно каким-либо образом изменять работу программного обеспечения, поэтому значение С не должно включаться ни в какое содержимое файла.

Поддержка DESTDIR обычно используется при создании пакетов. Она также полезна для пользователей, кто хочет понимать, где устанавливается данный пакет, и позволить пользователям, которые обычно не имеют разрешений установки в защищенные области файловой системы, выполнять сборку и предварительную (тестовую) установку до обладания этими разрешениями. И наконец, это может быть полезно для использования с такими инструментами, как stow, где код устанавливается в одном месте, но создается впечатление, что он установлен где-то еще с помощью символических ссылок или специальных операций монтирования. Поэтому настоятельно рекомендуется использовать пакеты GNU, поддерживающие DESTDIR, хотя это не является абсолютным требованием.

16.5. Переменные для директорий инсталляции

Директории инсталляции всегда должны получать имена через переменные, так чтобы можно было проще провести установку в нестандартное место. Стандартные имена для этих переменных и значения для них, которые должны быть в стандартных пакетах GNU, описываются далее. Они основаны на стандартной компоновке; варианты для них используются в GNU/Linux и других современных операционных системах.

Ожидается, что инсталляторы будут переназначать эти значения, когда вызывается make (например, make prefix=/usr install) или configure (например, configure --prefix=/usr). Пакеты GNU не должны пытаться угадывать, какое значение должно быть подходящим для этих переменных в системе, на которую они устанавливаются: используйте настройки по умолчанию, указанные здесь, чтобы все пакеты GNU вели себя идентично, позволяя инсталлятору достичь любой своей желаемой компоновки.

Все директории инсталляции и их родительские директории должны быть созданы (при необходимости) до установки в них.

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

prefix Префикс, используемый в конструировании значений по умолчанию перечисленных ниже переменных. Префикс по умолчанию переменной должен быть /usr/local. При построении полной системы GNU префикс будет пустым, и /usr будет символической ссылкой на / (если вы используете Autoconf, напишите это как '@prefix@').

   Запуск 'make install' с другим значением префикса, отличным от того, который использовался для сборки программы, не должен перекомпилировать программу.

exec_prefix Префикс, используемый в конструировании значений по умолчанию некоторых перечисленных ниже переменных. Значение по умолчанию exec_prefix должно быть $(prefix) (если вы используете Autoconf, напишите это как '@exec_prefix@').

   Обычно $(exec_prefix) используется для директорий, содержащих специфические для машины файлы (такие как исполняемый код и библиотеки подпрограмм), в то время как $(prefix) используется непосредственно для других директорий.

   Запуск 'make install' с другим значением exec_prefix отличающимся от того, которое использовалось для сборки программы, не должен перекомпилировать программу.

Исполняемые программы устанавливаются в одну из следующих директорий.

bindir Директория для установки исполняемых программ, которые может запускать пользователь. Обычно это должно быть /usr/local/bin, однако записывайте это как $(exec_prefix)/bin (если вы используете Autoconf, напишите это как '@bindir@').

sbindir Директория для установки исполняемых программ, которые могут запускаться из шелла, но обычно используются только администраторами системы. Это обычно должно быть /usr/local/sbin, однако напишите это как $(exec_prefix)/sbin (если вы используете Autoconf, напишите это как '@sbindir@').

libexecdir Директория для установки исполняемых программ, выполняемых другими программами, а не пользователями. Эта директория обычно должна быть /usr/local/libexec, но запищите её как $(exec_prefix)/libexec (если вы используете Autoconf, напишите это как '@libexecdir@').

   Определение 'libexecdir' одинаковое для всех пакетов, is the same for all packages, поэтому вы должны установить свои данные в его подкаталоге. Большинство пакетов устанавливаются свои данные в папку $(libexecdir)/package-name/, возможно в дополнительные подкаталоги, такие как $(libexecdir)/package-name/machine/version.

Файлы данных, используемые программой во время её выполнения, делятся на две категории двумя способами.

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

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

Вот переменные, которые Makefile должен использовать, чтобы указать каталоги для размещения этих различных видов файлов:

'datarootdir' Корень дерева директорий для архитектурно-независимых файлов данных, предназначенных только для чтения. Это должно обычно быть /usr/local/share, однако записывайте это как $(prefix)/share (если вы используете Autoconf, напишите это как '@datarootdir@'). Значение по умолчанию для 'datadir' основано на этой переменной; также 'infodir', 'mandir', и другие.

'datadir' Директория для установки уникальных файлов данных, не зависящих от архитектуры и доступных только для чтения, для этой программы. Это обычно то же самое, что и 'datarootdir', однако мы используем две отдельные переменные, чтобы вы могли перемещать эти специфичные для программы файлы, не изменяя место расположения файлов Info, страниц документации man pages, и т. п.

   Это должно обычно быть /usr/local/share, но записывайте это как $(datarootdir) (если вы используете Autoconf, напишите это как '@datadir@').

   Определение 'datadir' одинаковое для всех пакетов, так что вы должны установить свои данные в его подкаталоге. Большинство пакетов устанавливают свои данных в $(datadir)/package-name/.

'sysconfdir' Директория для установки read-only файлов данных, относящихся к одной машине, т. е. для конфигурирования хоста. Это файлы конфигурации почтовика и сети, /etc/passwd, и так далее. Все файлы в этой директории должны быть обычными текстовыми файлами ASCII. Эта директория должна быть обычно /usr/local/etc, однако записывайте это как $(prefix)/etc (если вы используете Autoconf, напишите это как '@sysconfdir@').

   Не устанавливайте в эту директорию исполняемые файлы (они скорее всего принадлежат каталогу $(libexecdir) или $(sbindir)). Также не устанавливайте сюда файлы, которые обычно модифицируются в процессе своего использования (программы, назначением которых является изменение конфигурации системы, исключаются). Они скорее всего принадлежат каталогу $(localstatedir).

'sharedstatedir' Директория для установки архитектурно-независимых файлов данных, которые программы модифицируют во время своей работы. Это должны быть обычно /usr/local/com, однако записывайте это как $(prefix)/com (если вы используете Autoconf, напишите это как '@sharedstatedir@').

'localstatedir' Директория для установки файлов данных, которые программы модифицируют в своей работе, и которые относятся к одной определенной машине. Пользователи никогда не должны изменять файлы в этой директории, чтобы конфигурировать работу пакета; помещайте такую конфигурационную информацию в отдельные файлы, расположенные в каталогах $(datadir) или $(sysconfdir). $(localstatedir) должна обычно быть /usr/local/var, однако записывайте это как $(prefix)/var (если вы используете Autoconf, напишите это как '@localstatedir@').

'runstatedir' Директория для установки файлов данных, которые программа модифицирует при своей работе, и которые относятся к одной определенной машине. Эти данные не должны сохраняться дольше, чем работает программа - это обычно длится долго, например до следующей перезагрузки. PID-файлы для системных демонов - это как раз такой случай использования. Дополнительно эта директория не должна очищаться, за исключением возможно случаев перезагрузки, в то время как общая временная директория /tmp (TMPDIR) может очищаться произвольно. Эта переменная обычно должна быть /var/run, однако записывайте её как $(localstatedir)/run. Наличие такой отдельной переменной позволяет при желании, например, использовать /run (если вы используете Autoconf 2.70 или более свежий, записывайте это как '@runstatedir@').

Эти переменные указывают директорию для инсталляции определенных специфичных типов файлов, если они есть у программы. Каждый пакет GNU должен иметь файлы Info, поэтому каждая программа нуждается в 'infodir', но не всем нужны 'libdir' или 'lispdir'.

'includedir' Директория для установки файлов заголовка, подключаемых программой C-директивой препроцессора #include. Это обычно должно быть /usr/local/include, однако записывайте как $(prefix)/include (если вы используете Autoconf, напишите это как '@includedir@'.)

   Большинство компиляторов, отличающихся от GCC не просматривают директорию /usr/local/include в поиске заголовочных файлов. Таким образом, установка файлов заголовка таким способом полезно только при использовании GCC. Иногда это не составляет проблему, потому что библиотеки фактически предназначены для работы только с GCC. Однако некоторые библиотеки предназначены для работы с другими компиляторами. Они должны установить свои заголовочные файлы в двух местах, одно указанное переменной includedir, и другое указанное oldincludedir.

'oldincludedir' Директория для установки заголовочных файлов #include, используемых компиляторами, не относящихся к GCC. Это обычно должно быть /usr/include (если вы используете Autoconf, то можете записать это как '@oldincludedir@').

   Команды Makefile должны проверить, пустое ли значение у oldincludedir. Если это так, то они не должны использовать эту переменную; они должны отменить вторичную инсталляцию заголовочных файлов.

   Пакет не должен заменять существующие файлы в этой директории, кроме случая, когда заголовочные файлы поступают из того же самого пакета. Таким образом, если пакет Foo предоставляет заголовочный файл foo.h, то он должен установить заголовочный файлы в директорию oldincludedir, если либо (1) в ней нет foo.h, или (2) файл существующий в ней файл заголовка foo.h поступил из того же пакета Foo.

   Чтобы указать, что foo.h поступил из пакета Foo, поместите magic-строку в file - как часть комментария - и выполните grep для обнаружения этой строки.

'docdir' Директория для установки файлов документации (не относящейся к Info) этого пакета. По умолчанию это должно быть /usr/local/share/doc/yourpkg, однако это следует записать как $(datarootdir)/doc/yourpkg (если вы используете Autoconf, напишите это как '@docdir@'). Подкаталог yourpkg, который может включать номер версии, предотвращает коллизии с общими именами, такими как README.

'infodir' Директория для установки файлов Info этого пакета. По умолчанию это должно быть /usr/local/share/info, однако должно быть записано как $(datarootdir)/info (если вы используете Autoconf, напишите это как '@infodir@'). Каталог infodir отделен от docdir для совместимости с существующей практикой.

'htmldir'
'dvidir'
'pdfdir'
'psdir' Директории для установки файлов документации в отдельных форматах. По умолчанию они должны быть установлены в $(docdir) (если вы используете Autoconf, запишите их как '@htmldir@', '@dvidir@', и т. п.). Пакеты, которые предоставляют несколько трансляций своей документации, должны установить их в '$(htmldir)/'ll, '$(pdfdir)/'ll, и т. д., где ll это аббревиатура локали, такая как 'en' или 'pt_BR'.

'libdir' Директория для объектных файлов и библиотек объектного кода. Не устанавливайте сюда исполняемые файлы, вместо этого они должны находиться в $(libexecdir). Значение libdir должно быть обычно /usr/local/lib, однако записывайте это как $(exec_prefix)/lib (если вы используете Autoconf, напишите это как '@libdir@').

'lispdir' Директория для установки файлов Emacs Lisp в этом пакете. По умолчанию это должно быть /usr/local/share/emacs/site-lisp, однако должно быть записано как $(datarootdir)/emacs/site-lisp. Если вы используете Autoconf, то запишите умолчание как '@lispdir@'. Чтобы работало make '@lispdir@', вам нужено добавить в свой файл configure.ac следующие строки:

lispdir='${datarootdir}/emacs/site-lisp'
AC_SUBST(lispdir)

'localedir' Директория для установки языковых каталогов сообщений этого пакета. По умолчанию это должно быть /usr/local/share/locale, однако должно быть записано как $(datarootdir)/locale (если вы используете Autoconf, напишите это как '@localedir@'). Обычно эта директория имеет подкаталог для каждого языкового стандарта.

Странички man-документации (Unix-style man pages) устанавливаются в одно из следующих мест:

'mandir' Директория верхнего уровня для установки man pages (если они имеются) для этого пакета. Это будет обычно /usr/local/share/man, но следует записывать как $(datarootdir)/man (если вы используете Autoconf, напишите это как '@mandir@').

'man1dir' Директория для установки section 1 man pages. Запишите это как $(mandir)/man1.

'man2dir' Директория для установки section 2 man pages. Запишите это как $(mandir)/man2.

'...' Не делайте основную документацию для любого программного обеспечения GNU как документацию страниц man (man page). Вместо этого напишите руководство Texinfo. Man-страницы предназначены только для людей, работающих с программным обеспечением GNU на Unix, что является второстепенным приложением.

'manext' Расширение имени файла для установленной man page. Это должно содержать точку, за которой идет подходящая цифра; обычно это должно быть '.1'.

'man1ext' Расширение имени файла для установленной section 1 man pages.

'man2ext' Расширение имени файла для установленной section 2 man pages.

'...' Используйте эти имена вместо 'manext', если пакет нуждается в установке man pages более чем одной секции руководства.

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

'srcdir' Директория для компилируемых исходников. Значение этой переменной обычно вставляется шелл-скриптом configure (если вы используете Autoconf, то используйте 'srcdir = @srcdir@'.)

Например:

# Общий префикс для директорий инсталляции.
# ВНИМАНИЕ: эта директория должна существовать, когда вы запускаете install.
prefix = /usr/local
datarootdir = $(prefix)/share
datadir = $(datarootdir)
exec_prefix = $(prefix)
# Сюда помещается исполняемый файл для команды 'gcc'.
bindir = $(exec_prefix)/bin
# Сюда помещаются директории, использумые компилятором.
libexecdir = $(exec_prefix)/libexec
# Сюда помещаются файлы Info.
infodir = $(datarootdir)/info

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

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

Иногда не все эти переменные могут быть реализованы в текущем релизе Autoconf и/или Automake; однако по состоянию Autoconf 2.60, скорее всего их реализация присутствует. Если что-либо из переменных отсутствует, описания здесь служат спецификациями того, что будет реализовывать Autoconf. Вы как программист можете либо использовать версию Autoconf для разработки, либо избегать использования этих переменных, пока не будет стабильный поддерживающий их релиз.

16.6. Стандартные целевые объекты для пользователей

Все программы GNU должны иметь следующие целевые объекты в своих файлах Makefile:

'all' Полная компиляция программы. Это должно быть целевым объектом по умолчанию (default target). Этот целевой объект не должен пересобирать никакие файлы документации; файлы Info должны быть обычно подключены в дистрибутив, и файлы DVI (и другого формата документации) должны быть сделаны только при явном запросе.

   По умолчанию правила Make должны компилировать и выполнять линковку с опцией '-g', чтобы исполняемые программы содержали символы отладки. Иначе вы будете в сущности беспомощны при крахе программы, который далеко не так легко воспроизвести с чистой сборкой.

'install' Компилирует программу и копирует исполняемые файлы, библиотеки, и так далее в имена файлов, предназначенные для реального использования. Если существует простой тест для проверки правильности установки программы, то этот целевой объект должен запустить его для тестирования.

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

   Если это возможно, то пишите целевое правило install таким образом, чтобы оно ничего не модифицировало в директории, где программа была собрана, если это было только что сделано командой 'make all'. Это удобно для сборки программы под одним именем пользователя и установке её под другим пользователем.

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

   Используйте '-' перед любой командой для установки страницы документации (man page), чтобы make игнорировала любые ошибки. Это для случая, если есть системы, в которых нет установленной системы документации Unix man page.

   Способ установки файлов Info состоит в их копировании в $(infodir) с $(INSTALL_DATA) (см. 16.3. Переменные для указания команд), и затем в запуске программы install-info, если она присутствует (install-info это программа, которая редактирует файл Info dir для добавления или обновления пункта меню для указанного файла Info; это часть пакета Texinfo).

   Ниже показано простой пример правила для установки файла Info, которое также пытается справиться с некоторыми дополнительными ситуациям, такими как отсутствие install-info.

do-install-info: foo.info installdirs
        $(NORMAL_INSTALL)
# Предпочтение файлу info в . для одной srcdir.
        if test -f foo.info; then d=.; \
         else d="$(srcdir)"; fi; \
        $(INSTALL_DATA) $$d/foo.info \
          "$(DESTDIR)$(infodir)/foo.info"
# Запуск install-info только если это имеется.
# Использование 'if' вместо подставки спереди '-', чтобы
# было оповещение о реальных ошибках из install-info.
# Используется '$(SHELL) -c', потому что некоторые шеллы
# не делают корректное завершение по ошибке, когда
# имеется неизвестная команда.
        $(POST_INSTALL)
        if $(SHELL) -c 'install-info --version' \
           >/dev/null 2>&1; then \
          install-info --dir-file="$(DESTDIR)$(infodir)/dir" \
                       "$(DESTDIR)$(infodir)/foo.info"; \
        else true; fi

   При написании целевого объекта install вы должны классифицировать все команды по трем категориям: обычные, команды перед инсталляцией и команды после инсталляции. См. 16.7. Категории команд установки.

'install-html'
'install-dvi'
'install-pdf'
'install-ps' Эти целевые объекты устанавливают документацию в форматах, не относящихся к Info; они предназначены для явного вызова персоной, устанавливающей пакет, если требуется определенный формат документации. GNU предпочитают файлы Info, которые должны инсталлироваться целевым объектом install.

   Когда у вас много файлов документации для установки, рекомендуется из избегать конфликтов и нагромождений, организуя установку этих целевых объектов для install в подкаталоги соответствующей директории инсталляции, такой как htmldir. В качестве одного примера, если у вашего пакета несколько руководств, и вы хотите установить HTML-документацию с множеством файлов (в таких как случаях режима вывода "split" команды makeinfo --html), определенно появится желание использовать подкаталоги, иначе два узла с одним и тем же именем в разных руководствах будут перезаписывать друг друга.

   Убедитесь, что создание этих install-format целевых объектов вызывали команды для целевого объекта format, например путем создания зависимости format.

'uninstall' Удаляет все инсталлированные файлы - копии, которые сделали целевые объекты 'install' и 'install-*'.

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

   Команды деинсталляции делятся на три категории, точно так же как и команды инсталляции. См. 16.7. Категории команд установки.

'install-strip' Работает наподобие install, но вырезает исполняемые файлы при их инсталляции. В простых случаях этот целевой объект может использовать целевой объект install следующим незамысловатым способом:

install-strip:
        $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' \
                install

   Однако если пакет устанавливает как скрипты, так и реальные исполняемые файлы, то целевой объект install-strip не может просто ссылаться на целевой объект install; он должен удалить исполняемые файлы, но не скрипты.

   Целевой объект install-strip не должен вырезать исполняемые файлы в директории сборки, которые копируются для установки. Вырезаться должны только установленные копии.

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

'clean' Удалит все файлы в текущей директории, которые обычно создаются при сборке программы. Также удаляет файлы в других директориях, если они были созданы этим makefile. Однако это не удаляет файлы, в которых записана конфигурация. Также сохраняются файлы, которые могут быть сделаны сборкой, но обычно не потому, что с ними поставляется дистрибутив. Нет необходимости удалять родительские каталоги, которые были созданы с помощью 'mkdir -p', поскольку они могли существовать в любом случае.

   Удалите здесь файлы .dvi, если они не составляют часть дистрибутива.

'distclean' Удаляет все файлы в текущей директории (или созданные этим makefile), которые были созданы конфигурированием или сборкой программы. Если у вас распакованные исходные файлы, и сборка программы происходит без создания каких-либо других файлов, то 'make distclean' должна оставить только файлы, которые были в дистрибутиве. Однако нет необходимости удалять родительские директории, которые были созданы командой 'mkdir -p', поскольку они могли существовать в любом случае.

'mostlyclean' Работает наподобие 'clean', однако может воздержаться от удаления некоторых файлов, которые люди обычно не хотят перекомпилировать. Например, целевой объект 'mostlyclean' для GCC не удаляет libgcc.a, потому что это редко необходимо, и занимает много времени.

'maintainer-clean' Удаляет почти все, что могло быть воссоздано этим Makefile. Это обычно включает все, что удаляет distclean, плюс дополнительные файлы: исходники C, созданные Bison, таблицы тегов, файлы Info, и так далее.

   Причина, по которой мы говорим "почти все", состоит в том, что запуск команды 'make maintainer-clean' не должен удалять configure, даже если configure может быть создан заново правилом в Makefile. В более общем смысле 'make maintainer-clean' не должен удалять ничего, что должно существовать для запуска configure, а затем начать сборку программы. Также нет необходимости удалять родительские директории, созданные 'mkdir -p', поскольку они могли существовать в любом случае. Это единственные исключения; maintainer-clean должен удалить все остальное, что может быть собрано заново.

   Целевой объект 'maintainer-clean' предназначен для использования сопровождающим пакета, а не обычными пользователями. Вам могут понадобиться специальные инструментальные утилиты для восстановления некоторых файлов, которые удаляет 'make maintainer-clean'. Поскольку эти файлы обычно включены в дистрибутив, мы не заботимся о том, чтобы их было легко восстановить. Если обнаружите, что вам нужно снова распаковать полный дистрибутив, не вините нас.

   Чтобы помочь пользователям осознать это команды для специального целевого объекта maintainer-clean должны начинаться со следующих двух команд:

@echo 'Эта команда предназначена для лиц, поддерживающих пакет; она' @echo 'удалит файлы, для создания которых нужны специальные инструменты.'

'TAGS' Обновит таблицу тегов для этой программы.

'info' Генерирует любые необходимые файлы Info. Лучше всего написать правила следующим образом:

info: foo.info
foo.info: foo.texi chap1.texi chap2.texi $(MAKEINFO) $(srcdir)/foo.texi

   Вы должны определить в Makefile переменную MAKEINFO. Она должна запускать программу makeinfo, которая входит как часть в дистрибутив Texinfo.

   Обычно дистрибутивы GNU поставляются с файлами Info, и это означает, что файлы Info присутствуют в каталоге исходного кода. Таким образом, правило Make для файла info должно обновлять его в исходной директории. Когда пользователи собирают пакет, When users build the package, обычно Make не обновляет файлы Info, потому что они уже будут обновленными.

'dvi'
'html'
'pdf'
'ps' Генерируют файлы документации в определенном формате. Эти целевые объекты всегда должны существовать, но любой из них или они все могут быть без операции (no-op), если выходной формат не может быть сгенерирован. Эти целевые объекты не должны быть зависимостями для целевого объекта all; пользователь должен их запускать вручную.

   Вот пример правила для генерации файлов DVI из Texinfo:

dvi: foo.dvi
foo.dvi: foo.texi chap1.texi chap2.texi $(TEXI2DVI) $(srcdir)/foo.texi

   Вы должны определить переменную TEXI2DVI в Makefile. Она должна запускать программу texi2dvi, которая входит как часть в дистрибутив Texinfo (texi2dvi использует TeX для реальной работы по форматированию. TeX не распространяется вместе с Texinfo). Альтернативно напишите только зависимости, и позвольте GNU make предоставить команду.

   Вот другой пример для генерации HTML из Texinfo:

html: foo.html
foo.html: foo.texi chap1.texi chap2.texi $(TEXI2HTML) $(srcdir)/foo.texi

   И опять-таки, вы должны в Makefile определить переменную TEXI2HTML; например, она может запускать makeinfo --no-split --html (makeinfo входит как часть в дистрибутив Texinfo).

'dist' Создаст tar-файл дистрибутива для этой программы. Этот tar-файл должен быть установлен таким образом, чтобы имена в tar-файле начинались с имени подкаталога, которое является именем пакета, для которого он предназначен. Это имя может включать номер версии.

   Например, распространяемый tar-файл компилятора GCC версии 1.40 распаковывается в поддиректорию с именем gcc-1.40.

   Самый простой способ сделать это - создать подкаталог с соответствующим именем, использовать ln или cp для установки в него правильных файлов, и затем выполнить tar для этого подкаталога.

   Выполните сжатие tar-файла с помощью gzip. Например, реальный файл дистрибутива для GCC версии 1.40 называется gcc-1.40.tar.gz. Будет нормальным также поддерживать бесплатные форматы сжатия.

   Целевой объект dist должен явно зависеть от всех исходных файлов в дистрибутиве, чтобы гарантировать актуальность дистрибутива. См. 7.3 Making Releases в описании стандартов кодирования GNU [12].

'check' Выполнит самотестирование (self-tests, если это есть). Пользователь должен выполнить сборку программы перед запуском тестов, но установка программы не требуется; вы должны написать self-tests таким образом, чтобы они работали, когда программа собрана, но не установлена.

Следующие целевые объекты предлагаются как в качестве обычных имен для программ, в которых они полезны.

installcheck Выполнит тесты инсталляции (если они есть). Пользователь должен выполнить сборку и установку программы перед запуском этих тестов. Вы не должны полагаться на то, что $(bindir) находится в пути поиска.

installdirs Бывает полезно добавить целевой объект с именем 'installdirs' для создания директорий, куда инсталлируются файлы, а также их родительских директорий. Вот удобный для этого скрипт с именем mkinstalldirs; вы можете встретить его в пакете Gnulib. Можно использовать следующее правило:

# Гарантирует наличие всех директорий установки (например $(bindir))
# путем их создания при необходимости.
installdirs: mkinstalldirs
        $(srcdir)/mkinstalldirs $(bindir) $(datadir) \
                                $(libdir) $(infodir) \
                                $(mandir)

   ... или, если вы хотите поддерживать DESTDIR (что настоятельно рекомендуется),

# Гарантирует наличие всех директорий установки (например $(bindir))
# путем их создания при необходимости.
installdirs: mkinstalldirs
        $(srcdir)/mkinstalldirs \
            $(DESTDIR)$(bindir) $(DESTDIR)$(datadir) \
            $(DESTDIR)$(libdir) $(DESTDIR)$(infodir) \
            $(DESTDIR)$(mandir)

   Это правило не должно модифицировать директории, где осуществляется компиляция. Ничего не должно делаться кроме создания директорий инсталляции.

16.7. Категории команд установки

При написании целевого объекта install вы должны классифицировать все команды по трем категориям: обычные команды, команды перед установкой (pre-installation) и команды после установки (post-installation).

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

Команды pre-installation и post-installation могут изменять другие файлы; в частности, они могут редактировать файлы глобальной конфигурации или базы данных.

Команды pre-installation обычно выполняются перед обычными командами, а команды post-installation как правило запускаются после обычных команд.

Наиболее распространенным способом использования команды post-installation - запуск install-info. Это не может быть сделано обычной командой, поскольку изменит файл (каталог Info), который не приходит полностью и исключительно из устанавливаемого пакета. Это команда post-installation, потому что она должна быть завершена после обычной команды, которая установит файлы Info пакета.

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

Для классифицирования команд в правиле install по этим трем категориям, вставьте между ними строки category. Строка category указывает категорию команд, которые за ней последуют.

Строка category состоит из символа tab и ссылки на специальную переменную Make, плюс опциональный комментарий в конце. Можно использовать три переменные, по одной для каждой категории. Строки категории являются no-ops в обычном выполнении, поскольку эти три Make-переменные обычно не определены (и их не следует определять в makefile).

Вот три возможные строки category, каждая с комментарием, объясняющим её смысл:

        $(PRE_INSTALL)     # Дальше идут команды pre-install.
        $(POST_INSTALL)    # Дальше идут команды post-install.
        $(NORMAL_INSTALL)  # Дальше идут обычные команды.

Если вы не используете строку category в начале правила install, то все команды инсталляции классифицируются как нормальные до первой строки category. Если вы не используете никакие строки category, то все команды классифицируются как нормальные.

Вот строки category для uninstall:

        $(PRE_UNINSTALL)     # Дальше идут команды pre-uninstall.
        $(POST_UNINSTALL)    # Дальше идут команды post-uninstall.
        $(NORMAL_UNINSTALL)  # Дальше идут обычные команды.

Обычно команда pre-uninstall используется для предварительного удаления элементов из директории Info.

Если целевой объект install или uninstall имеет любые зависимости, которые действуют как подпрограммы инсталляции, то вы должны начать команды каждой зависимости со строки category, и начать основные целевые команды также со строки category. Таким способом вы гарантируете, что каждая команда помещается в правильную категорию независимо от того, какая зависимость выполняется на самом деле.

Команды pre-installation и post-installation не должны запускать никакие программы кроме следующих:

[ basename bash cat chgrp chmod chown cmp cp dd diff echo
expand expr false find getopt grep gunzip gzip
hostname install install-info kill ldconfig ln ls md5sum
mkdir mkfifo mknod mv printenv pwd rm rmdir sed sort tee
test touch true uname xargs yes

Причина такого разграничения команд - обеспечение создание двоичных пакетов. Обычно двоичный пакет содержит все исполняемые и другие файлы, которые должны быть установлены, и имеет свой собственный метод их установки - поэтому ему не нужно запускать обычные команды инсталляции. Но при установке двоичного пакета необходимо выполнить команды pre-installation и post-installation.

Программы для сборки двоичных пакетов работают путем извлечения команд pre-installation и post-installation. Ниже показан один из способов извлечения команд pre-installation (опция -s для make нужна для подавления сообщений по входу в подкаталоги):

make -s -n install -o all \
      PRE_INSTALL=pre-install \
      POST_INSTALL=post-install \
      NORMAL_INSTALL=normal-install \
  | gawk -f pre-install.awk
.. здесь файл pre-install.awk может содержать:
$0 ~ /^(normal-install|post-install)[ \t]*$/ {on = 0} on {print $0} $0 ~ /^pre-install[ \t]*$/ {on = 1}

[Ссылки]

1. GNU make.
2. Руководство GNU make: главы 1-3.

 

Добавить комментарий


Защитный код
Обновить

Top of Page