Глава 13. Что делать нужно, и что делать нельзя

Этот перевод может быть устаревшим. Для того, чтобы помочь с переводом, пожалуйста, обратитесь к Сервер переводов FreeBSD.

13.1. Введение

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

13.2. WRKDIR

Не пишите ничего в файлы вне каталога WRKDIR. Каталог WRKDIR является единственным местом, которое гарантированно будет доступно для записи во время построения порта (обратитесь к главе об установке портов с CDROM за примером построения портов из дерева, доступного только для чтения). Если вам нужно изменить какой-либо из файлов pkg-*, сделайте это, переопределив переменную, но не перезаписывая их.

13.3. WRKDIRPREFIX

Добейтесь того, чтобы ваш порт принимал во внимание значение переменной WRKDIRPREFIX. Большинство портов об этом не заботятся. В частности, если вы обращаетесь к каталогу WRKDIR другого порта, заметьте, что его правильным местоположением является WRKDIRPREFIXPORTSDIR/subdir/name/work, а не PORTSDIR/subdir/work или .CURDIR/../../subdir/name/work мли что-то подобное. Кроме того, если вы сами задаете WRKDIR, то должны поставить перед ним знак ${WRKDIRPREFIX}${.CURDIR}.

13.4. Различение операционных систем и версий ОС

Вы можете встретиться с кодом, который требует модификаций или условной компиляции в зависимости от того, с какой версией FreeBSD Unix он работает. Предпочтительным способом отделения кода для версий FreeBSD является использование макросов __FreeBSD_version и __FreeBSD__, определённых в sys/param.h. Если этот файл не подключен, добавьте код

#include <sys/param.h>

в нужном месте файла .c.

__FreeBSD__ определён во всех версиях FreeBSD в качестве старшего номера версии системы. Например, в FreeBSD 9.x __FreeBSD__ определён со значением 9.

#if __FreeBSD__ >= 9
#  if __FreeBSD_version >= 901000
	 /* 9.1+ release specific code here */
#  endif
#endif

Полный список значений __FreeBSD_version доступен в Значения __FreeBSD_version.

13.5. Написание чего-либо после bsd.port.mk

Не пишите ничего после строки .include <bsd.port.mk>. Этой строки можно избежать, включив в где-то в середину вашего файла Makefile файл bsd.port.pre.mk, и файл bsd.port.post.mk в конец.

Вам нужно включить либо пару файлов bsd.port.pre.mk/bsd.port.post.mk, либо только bsd.port.mk; не используйте оба этих метода одновременно.

В файле bsd.port.pre.mk определяются лишь несколько переменных, которые могут быть использованы в тестах из файла Makefile, в файле bsd.port.post.mk заданы остальные.

Вот некоторые важные переменные, определенные в файле bsd.port.pre.mk (это не полный список, для выяснения полного списка прочтите, пожалуйста, сам файл bsd.port.mk).

ПеременнаяОписание

ARCH

Архитектура машины в виде, получаемом по команде uname -m (например, i386)

OPSYS

Тип операционной системы, получаемый по команде uname -s (например, FreeBSD)

OSREL

Версия релиза операционной системы (например, 2.1.5 или 2.2.7)

OSVERSION

Версия операционной системы в виде числа, та же, что и __FreeBSD_version.

LOCALBASE

Корень дерева "local" (например, /usr/local)

PREFIX

Куда, собственно, устанавливается порт (обратитесь к подробной информации о PREFIX).

Если вы задаете переменную MASTERDIR, делайте это до подключения bsd.port.pre.mk.

Вот несколько примеров того, что вы можете написать после bsd.port.pre.mk:

# no need to compile lang/perl5 if perl5 is already in system
.if ${OSVERSION} > 300003
BROKEN=	perl is in system
.endif

Вы не забываете об использовании табуляции вместо пробелов после BROKEN=.

13.6. Использование выражения exec в сценариях обёртках

Если порт устанавливает сценарий на языке shell, который служит для запуска другой программы, и если запуск этой программы является последним действием сценария, убедитесь, что запуск программы производится с использованием выражения exec, например:

#!/bin/sh
exec %%LOCALBASE%%/bin/java -jar %%DATADIR%%/foo.jar "$@"

Выражение exec заменяет процесс сценария на указанную программу. Если exec опущен, то процесс сценария во время работы программы остается в памяти, бесполезно потребляя системные ресурсы.

13.7. Поступайте разумно

Файл Makefile должен выполнять действия просто и небеспричинно. Если вы можете сделать что-то на несколько строк короче или более читабельно, сделайте это. В качестве примеров можно привести использование конструкций .if утилиты make вместо соответствующей конструкции if командного процессора, ненужность переопределения цели do-extract при возможности переопределения EXTRACT* и использование GNU_CONFIGURE вместо CONFIGURE_ARGS += --prefix=${PREFIX}.

Если вы обнаружите, что для выполнения чего-то приходится писать много нового кода, то, пожалуйста, просмотрите файл bsd.port.mk на предмет того, не содержит ли он решение именно вашей проблемы. Хотя его трудно читать, имеется много проблем, выглядящих сложными, для которых файл bsd.port.mk уже содержит быстрое решение.

13.8. Относитесь внимательно как к CC, так и CXX

Порт должен принимать во внимание как переменную CC, так и CXX. Под этим мы подразумеваем, что порт ни в коем случае не должен устанавливать значения этих переменных, переопределяя имеющиеся значения; вместо этого можно добавлять нужные значения к уже имеющимся. Это связано с тем, что параметры построения, относящиеся ко всем портам, могут быть заданы глобально.

Если порт не учитывает значения этих переменных, добавьте строку NO_PACKAGE=ignores either cc or cxx в файл Makefile.

Далее следует пример файла Makefile, использующего как переменную CC, так и CXX. Обратите внимание на использование символов ?=:

CC?= gcc
CXX?= g++

Вот пример, в котором не принимаются во внимание ни CC, ни CXX:

CC= gcc
CXX= g++

В системах FreeBSD обе переменные CC и CXX могут быть определены в файле /etc/make.conf. В первом примере задаётся значение, если оно ранее не было определено в /etc/make.conf, что сохраняет любые определения, данные на уровне системы в целом. Второй пример переопределяет всё, что было задано ранее.

13.9. Относитесь внимательно к CFLAGS

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

Если порт не учитывает значения этой переменной, добавьте строку NO_PACKAGE=ignores cflags в файл Makefile.

Далее следует пример файла Makefile, использующего переменную CFLAGS. Обратите внимание на использование символов +=:

CFLAGS+= -Wall -Werror

А вот пример, в котором не учитывается значение переменной CFLAGS:

CFLAGS= -Wall -Werror

В системе FreeBSD переменная CFLAGS определена в файле /etc/make.conf. В первом примере к переменной CFLAGS добавляются дополнительные флаги, при этом сохраняются все определения, данные ранее на уровне системы. Во втором примере всё, что было задано ранее, игнорируется.

Из сторонних файлов Makefile следует удалить флаги оптимизации. Общесистемные флаги оптимизации находятся в системной переменной CFLAGS. Пример из немодифицированного Makefile:

CFLAGS= -O3 -funroll-loops -DHAVE_SOUND

При использовании системных флагов оптимизации Makefile станет похожим на следующий пример:

CFLAGS+= -DHAVE_SOUND

13.10. Подробные логи сборки

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

Пример неинформативного лога сборки (плохой):

  CC     source1.o
  CC     source2.o
  CCLD   someprogram

Пример подробного журнала сборки (хороший):

cc -O2 -pipe -I/usr/local/include -c -o source1.o source1.c
cc -O2 -pipe -I/usr/local/include -c -o source2.o source2.c
cc -o someprogram source1.o source2.o -L/usr/local/lib -lsomelib

Некоторые системы сборки, такие как CMake, ninja и GNU configure, настроены на подробное ведение журнала в рамках инфраструктуры портов. В других случаях портам могут потребоваться индивидуальные изменения.

13.11. Обратная связь

Посылайте подходящие изменения/патчи автору/сопровождающему для включения в следующий релиз. Это только сделает вашу работу гораздо легче при выходе следующего релиза.

13.12. README.html

README.html не является частью порта и генерируется при помощи make readme. Не включайте этот файл в патчи или коммиты.

Если не удается выполнить make readme, убедитесь, что значение по умолчанию ECHO_MSG не изменено внутри порта.

13.13. Пометка неустанавливаемого порта как BROKEN, FORBIDDEN или IGNORE

В некоторых случаях пользователи не должны допускаться к установке порта. Для того, чтобы сообщить пользователю, что порт не следует устанавливать, имеется несколько make-переменных, которые могут быть использованы в файле Makefile порта. Значения следующих make-переменных будут причиной, возвращаемой пользователям, по которой порт отказывает в установке. Пожалуйста, используйте корректные make-переменные, так как каждая переменная make передает абсолютно различный смысл как для пользователей, так и для автоматизированных систем, которые полагаются на файлы Makefile, таких как кластер построения портов, FreshPorts.

13.13.1. Переменные

  • BROKEN предназначена для портов, которые в настоящее время не компилируются, не устанавливаются или не удаляются правильно. Следует использовать, когда проблема считается временной.

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

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

    • не компилируется

    • выполняет со сбоем конфигурирование или процесс установки

    • устанавливает файлы вовне ${PREFIX}

    • не удаляет полностью все свои файлы при деинсталляции (тем не менее, это может быть допустимо, и подходит для портов, оставляющих после себя файлы, измененные пользователем)

    • имеет проблемы во время выполнения на системах, где он должен работать нормально.

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

  • IGNORE предназначена для портов, которые не должны строиться по какой-либо другой причине. Следует использовать для портов, в случае когда проблема считается структурной. Кластер построения ни при каких условиях не будет строить порты, помеченные как IGNORE. В частности, используйте IGNORE, когда порт:

    • не работает на установленной версии FreeBSD

    • имеет distfile, который не может быть автоматически загружен из-за ограничений лицензирования

    • не работает с некоторыми другими установленными портами (например, порт зависит от www/drupal7, но установлен www/drupal8)

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

13.13.2. Заметки о реализации

Не заключайте значения переменных BROKEN, IGNORE и связанных с ними в кавычки. Из-за способа отображения информации пользователю, формулировка сообщений для каждой переменной отличается:

BROKEN=	fails to link with base -lcrypto
IGNORE=	unsupported on recent versions

и в результате make describe выведен информацию в таком виде :

===>  foobar-0.1 is marked as broken: fails to link with base -lcrypto.
===>  foobar-0.1 is unsupported on recent versions.

13.14. Архитектурные соображения

13.14.1. Общие замечания об архитектурах

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

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

cd ${SRCDIR}; make targets

Значения отображаются в форме TARGET/TARGET_ARCH. Переменная только для чтения ARCH в ports устанавливается на основе значения TARGET_ARCH. Makefile в портах должны проверять значение этой переменной.

13.14.2. Пометка порта как архитектурно нейтрального

Порты, которые не имеют зависимых от архитектуры файлов или требований, определяются установкой NO_ARCH=yes.

Пакеты, собранные из таких портов, имеют строку архитектуры, оканчивающуюся на :* (архитектура с подстановочным символом), в отличие от, например, freebsd:13:x86:64 (архитектура amd64).

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

13.14.3. Пометка порта как игнорируемого только на определенных архитектурах

  • Чтобы пометить порт как IGNORE только для определенных архитектур, существуют две другие удобные переменные, которые автоматически устанавливают IGNORE: ONLY_FOR_ARCHS и NOT_FOR_ARCHS. Примеры:

    ONLY_FOR_ARCHS=	i386 amd64
    NOT_FOR_ARCHS=	ia64 sparc64

    Пользовательское сообщение IGNORE можно задать с помощью ONLY_FOR_ARCHS_REASON и NOT_FOR_ARCHS_REASON. Для отдельных архитектур возможны записи с ONLY_FOR_ARCHS_REASON_ARCH и NOT_FOR_ARCHS_REASON_ARCH.

  • Если порт загружает и устанавливает бинарные файлы i386, установите IA32_BINARY_PORT. Если эта переменная задана, /usr/lib32 должен присутствовать для IA32-версий библиотек, а ядро должно поддерживать совместимость с IA32. Если одно из этих двух условий не выполняется, IGNORE будет установлен автоматически.

13.14.4. Специфические аспекты для кластеров

  • Некоторые порты пытаются оптимизировать себя под конкретную машину, на которой они собираются, указывая компилятору -march=native. Этого следует избегать: либо добавить этот параметр в опцию, отключенную по умолчанию, либо удалить его полностью.

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

13.15. Пометка порта на удаление с DEPRECATED или EXPIRATION_DATE

Помните, что BROKEN и FORBIDDEN будут использованы как временное средство, если порт не является работающим. Постоянно неработоспособные порты должны полностью удаляться из дерева.

В подходящих ситуациях пользователи могут быть оповещены о предстоящем удалении через переменные DEPRECATED и EXPIRATION_DATE. Первое - это просто строка, сообщающая причину запланированного удаления порта; вторая является строкой в формате ISO 8601 (YYYY-MM-DD). Обе будут показаны пользователю.

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

При пометке порта как DEPRECATED, если существуют альтернативные порты, которые можно использовать вместо устаревающего, удобно упомянуть их в сообщении коммита.

Не существует установленного правила о том, насколько заранее нужно уведомлять. Текущая практика предполагает один месяц для проблем, связанных с безопасностью, и два месяца для проблем сборки. Это также дает заинтересованным коммиттерам немного времени на исправление проблем.

13.16. Избегайте использования конструкции .error

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

Использование для этих целей .error является распространенной ошибкой. Проблема в том, что в этой ситуации будут повреждены многие инструменты автоматизации, работающие с деревом портов. Наибольшим образом это распространено при попытке построить /usr/ports/INDEX (смотрите Запуск make describe). Тем не менее, даже более простые команды, такие как make maintainer, в этом случае также вернут ошибку. Это не является приемлемым.

Пример 1. Как избегать использования .error

Из следующих двух вариантов строки файла Makefile первый приведёт к неудачному завершению работы make index, а второй - нет:

.error "option is not supported"
IGNORE=option is not supported

13.17. Использование sysctl

Использование sysctl не рекомендуется, кроме как при выполнении целей. Это вызвано тем, что вычисление любых makevar, таких как во время команды make index, с необходимостью запуска этой команды, еще больше замедляет весь процесс.

sysctl(8) следует всегда использовать через переменную SYSCTL, поскольку она содержит полностью заданный путь, и при необходимости может быть переопределена.

13.18. Меняющиеся дистрибутивные файлы

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

Отложите старый distfile в сторону, загрузите новый, распакуйте их и сравните содержимое с помощью diff(1). Если ничего подозрительного нет, обновите distinfo.

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

Связжитесь с автором этого программного обеспечения для подтверждения изменений.

13.19. Используйте стандарты POSIX

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

Не используйте /proc, если есть другие способы получить информацию. Например, setprogname(argv[0]) в main(), а затем getprogname(3) для получения имени исполняемого файла.

Не полагайтесь на поведение, не документированное в POSIX.

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

Ряд простых системных вызовов (например, gettimeofday(2), getpid(2)) работают намного быстрее в Linux® по сравнению с любой другой операционной системой из-за кэширования и используемой оптимизации vsyscall. Не полагайтесь на их дешевизну в критичных к производительности приложениях. В целом, старайтесь избегать системных вызовов там, где это возможно.

Не полагайтесь на специфичное для Linux® поведение сокета. В частности, отличаются размеры буфера сокета по умолчанию (выполните вызов setsockopt(2) с SO_SNDBUF и SO_RCVBUF, и в то время как в Linux® при заполнении буфера сокета send(2) блокируется, FreeBSD возвращает ошибку и устанавливает ENOBUFS в качестве значения errno.

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

Используйте страницы справочника для проверки, относится ли функция к интерфейсу POSIX (ищите раздел "STANDARDS" на странице справочника).

Не рассчитывайте на то, что в качестве /bin/sh используется bash. Убедитесь, что командная строка, переданная в system(3), будет работать в POSIX-совместимой оболочке.

Список основных bash-измов расположен здесь.

Проверьте, что используемые заголовочные файлы включены в POSIX или список, рекомендуемый страницей справочника, т.к. например, забыть подключить sys/types.h - не такая уж проблема в Linux®, однако это не так во FreeBSD.

13.20. Разное

Файлы pkg-descr и pkg-plist должны проверяться дважды. Если вы пересматриваете порт и думаете, что его можно описать иначе, сделайте это.

Будьте внимательны с юридическими вопросами! Не делайте из нас нелегальных распространителей ПО!


Изменено: 18 сентября 2025 г. by Vladlen Popolitov