---
description: 'Описание создания порта FreeBSD, когда программа требует некоторых изменений'
next: books/porters-handbook/makefiles
params:
  path: /books/porters-handbook/slow-porting/
prev: books/porters-handbook/quick-porting
showBookMenu: 'true'
tags: ["porting", "ports", "slow porting", "guide"]
title: 'Глава 4. Медленное портирование'
weight: 4
---

[[slow-porting]]
= Медленное портирование
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumoffset: 4
:partnums:
:source-highlighter: rouge
:experimental:
:images-path: books/porters-handbook/

ifdef::env-beastie[]
ifdef::backend-html5[]
:imagesdir: ../../../../images/{images-path}
endif::[]
ifndef::book[]
include::shared/authors.adoc[]
include::shared/mirrors.adoc[]
include::shared/releases.adoc[]
include::shared/attributes/attributes-{{% lang %}}.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/urls.adoc[]
toc::[]
endif::[]
ifdef::backend-pdf,backend-epub3[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
endif::[]

ifndef::env-beastie[]
toc::[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]

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

[[slow-work]]
== Как всё это работает

Во-первых, когда пользователь даёт в своём каталоге с портом команду `make`, происходит целая череда событий. Во время чтения этого текста может оказаться полезным иметь файл [.filename]#bsd.port.mk# открытым в другом окне, что сильно поможет его понять.

Но не волнуйтесь сильно, если вы не до конца понимаете, что делается в [.filename]#bsd.port.mk#, не так уж много людей его понимает... _:-)_

[.procedure]
. Запускается цель `fetch`. Цель `fetch` отвечает за то, что архив исходных текстов имеется в наличии локально в каталоге `DISTDIR`. Если цель `fetch` не может найти требуемые файлы в каталоге `DISTDIR`, то они будут искаться по указателю URL `MASTER_SITES`, который устанавливается в Makefile, а также на наших FTP зеркалах, куда мы по возможности помещаем дистрибутивные файлы для архива. Затем она попытается сгрузить указанный файл с помощью `FETCH`, полагая, что запрашивающая машина имеет прямое подключение к Интернет. Если файл скачается удачно, то он будет помещен в каталог `DISTDIR` для последующего использования и обработки.
. Выполняется цель `extract`. Она ищет дистрибутивный файл порта (как правило, tar-архив `gzip`) в каталоге `DISTDIR` и распаковывает его во временный каталог, задаваемый переменной `WRKDIR` (по умолчанию [.filename]#work#).
. Выполняется цель `patch`. Во-первых, применяются все патчи, заданные переменной `PATCHFILES`. Во-вторых, если какие-либо файлы с патчами, носящие имена [.filename]#patch-*#, имеются в подкаталоге `PATCHDIR` (по умолчанию это каталог [.filename]#files#), то они применяются в этот момент в алфавитном порядке.
. Запускается цель `configure`. Здесь может выполняться любая из многих различных вещей.
.. Если он существует, запускается [.filename]#scripts/configure#.
.. Если установлены `HAS_CONFIGURE` или `GNU_CONFIGURE`, запускается [.filename]#WRKSRC/configure#.

. Выполняется цель `build`. Она отвечает за переход в собственный рабочий каталог порта (`WRKSRC`) и его построение.
. Выполняется цель `stage`. Конечный набор построенных файлов помещается во временный каталог (`STAGEDIR`, смотрите crossref:special[staging,Staging]). Иерархия этого каталога отражает иерархию каталогов системы, в которую данный пакет будет устанавливаться.
. Выполняется цель `package`. При этом создаётся пакет с использованием файлов из временного каталога, созданного во время выполнения цели `stage`, и файла [.filename]#pkg-plist# порта.
. Выполняется цель `install`. Это устанавливает пакет, созданный во время цели `package`, в хост-систему.

Выше перечислены стандартные действия. Кроме того, вы сами можете определить цели `pre-_что-то_` или `post-_что-то_`, или создать скрипты с такими именами в подкаталоге [.filename]#scripts#, и они будут запущены до или после выполнения действий по умолчанию.

Например, если у вас есть цель `post-extract`, определённая в вашем файле [.filename]#Makefile# и файл [.filename]#pre-build# в подкаталоге [.filename]#scripts#, то после выполнения обычных действий по распаковке, будет вызвана цель `post-extract` а скрипт [.filename]#pre-build# будет выполнен перед запуском стандартных правил построения. Рекомендуется использовать цели из [.filename]#Makefile#, если действия достаточно просты, потому что в дальнейшем будет проще определить, какие нестандартные действия требует порт.

Действия по умолчанию выполняются целями `do-_что-то_` из [.filename]#bsd.port.mk#. Например, команды для распаковки порта находятся в цели `do-extract`. Если вам не хватает цели по умолчанию, вы можете её исправить, переопределив цель `do-_something_` в вашем файле [.filename]#Makefile#.

[NOTE]
====
"Основные" цели (к примеру, `extract`, `configure` и так далее) не делают ничего больше, чем проверяют успешность завершения всех предыдущих шагов и вызывают настоящие цели или скрипты, и их не нужно менять. Если вам нужно изменить распаковку, исправляйте `do-extract`, но никогда не меняйте способ работы `extract`! Кроме того, цель `post-deinstall` является недействительной и не выполняется инфраструктурой портов.
====

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

[[slow-sources]]
== Получение исходного кода

Получите оригинальные исходные тексты (обычно) в виде упакованного tar-архива ([.filename]#foo.tar.gz# или [.filename]#foo.tar.bz2#) и скопируйте его в каталог `DISTDIR`. Всегда используйте исходные тексты _основной ветки разработки_ везде, где это возможно.

Вам потребуется задать значение переменной `MASTER_SITES` так, чтобы оно указывало на местоположение оригинального tar-архива. В файле [.filename]#bsd.sites.mk# вы найдёте краткие обозначения для большинства популярных сайтов. Пожалуйста, используйте эти сайты-и соответствующие определения-везде, где это возможно, чтобы избежать проблем повторения одной и той же информации в базе источников. Так как эти сайты со временем меняются, для всех причастных поддержка становится настоящим кошмаром. Для подробностей смотрите crossref:makefiles[makefile-master_sites,`MASTER_SITES`].

Если вы не можете найти FTP/HTTP сайт с хорошим подключением к сети, или находите только сайты, которые имеют раздражающе нестандартные форматы, то можете захотеть поместить копию на надёжный сервер FTP или HTTP, который вам доступен (например, ваша домашняя страница).

Если вы не можете найти доступного и надёжного места для помещения дистрибутивного файла, то мы сами сможем разместить его на сервере `ftp.FreeBSD.org`; однако это наименее рекомендуемое решение. Дистрибутивный файл должен быть помещён в каталог [.filename]#~/public_distfiles/# одного из пользователей машины `freefall`. Попросите того, кто коммитил ваш порт, сделать это. Этот человек также задаст переменной `MASTER_SITES` значение `MASTER_SITE_LOCAL`, а в переменной `MASTER_SITE_SUBDIR` укажет логин кластера FreeBSD.

Если дистрибутивные файлы вашего порта постоянно меняются по неизвестным причинам без изменения версий со стороны автора, остаётся только поместить дистрибутив на вашу домашнюю Web-страницу и указать её первой в списке `MASTER_SITES`. Если можете, попытайтесь договориться с автором порта об этом; это действительно помогает в достижении некоторого управления исходным кодом. Размещение собственной версии поможет избежать появления ошибок у пользователей типа `checksum mismatch`, а также уменьшит нагрузку на людей, сопровождающих наш FTP-сервер. Также, если у порта имеется только один основной сервер, то рекомендуется поместить архивную копию на свой сайт и указать его в списке `MASTER_SITES` вторым.

Если вашему порту требуются дополнительные `патчи`, доступные в Интернет, скачайте также и их, поместив в каталог `DISTDIR`. Не волнуйтесь, если они находятся не на том же сайте, откуда взят дистрибутивный архив, мы умеем обрабатывать такие ситуации (смотрите описание crossref:makefiles[porting-patchfiles,PATCHFILES] ниже).

[[slow-modifying]]
== Модификация порта

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

Если вашему порту во время компиляции, установки и настройки требуется довольно много взаимодействовать с пользователем, то посмотрите на один из классических скриптов Configure Лэрри Уолла (Larry Wall) и сделайте сами что-либо подобное. Предназначение новой коллекции портов - это сделать каждое приложение в стиле "plug-and-play" настолько, насколько это вообще возможно для конечного пользователя при минимальном использовании дискового пространства.

[NOTE]
====
Если явно не указано обратное, то патчи, скрипты и другие файлы, которые вы создали и предоставили для Коллекции Портов FreeBSD, неявно подпадают под стандартные условия лицензии BSD.
====

[[slow-patch]]
== Работа с патчами

Файлы, которые добавлялись или изменялись в процессе создания порта, могут быть выявлены программой man:diff[1], а результат работы этой программы может быть в дальнейшем передан программе man:patch[1]. Такое действие с обычным файлом подразумевает сохранение копии файла с первоначальным содержимым перед внесением каких-либо изменений.

[source, shell]
....
% cp file file.orig
....

Патчи сохраняются в виде файлов с именем [.filename]#patch-*#, где _*_ обозначает путь к файлу, к которому применяется патч, такой как [.filename]#patch-Imakefile# или [.filename]#patch-src-config.h#.

[TIP]
====
Используйте `BINARY_ALIAS` для замены жёстко заданных команд во время сборки и избежания исправлений в файлах сборки. Подробнее см. в crossref:makefiles[binary-alias,Использование `BINARY_ALIAS` для переименования команд вместо исправления сборки].
====

[[slow-patch-rules]]
=== Общие правила для установки патчей

Файлы патчей хранятся в `PATCHDIR`, обычно это [.filename]#files/#, откуда они будут автоматически применены. Все исправления должны быть относительны к `WRKSRC`. Обычно `WRKSRC` является подкаталогом `WRKDIR`, каталога, в котором распаковывается distfile. Используйте `make -V WRKSRC` для просмотра фактического пути. Имена файлов патчей должны соответствовать следующим правилам:

* Избегайте ситуации, когда несколько патчей изменяют один и тот же файл. Например, если и [.filename]#patch-foobar.c#, и [.filename]#patch-foobar.c2# вносят изменения в [.filename]#${WRKSRC}/foobar.c#, это делает их хрупкими и затрудняет отладку.
* При создании имён для файлов исправлений заменяйте каждое подчеркивание (`\_`) на два подчеркивания (`\__`) и каждый слэш (`/`) на одно подчеркивание (`_`). Например, чтобы исправить файл с именем [.filename]#src/freeglut_joystick.c#, назовите соответствующий исправление [.filename]#patch-src_freeglut__joystick.c#. Не называйте исправления как [.filename]#patch-aa# или [.filename]#patch-ab#. Всегда используйте путь и имя файла в названиях исправлений. Использование `make makepatch` автоматически генерирует правильные имена.
* Патч может изменять несколько файлов, если изменения связаны между собой и патч назван соответствующим образом. Например, [.filename]#patch-add-missing-stdlib.h#.
* Используйте только символы `[-+.\_a-zA-Z0-9]` для именования патчей. В частности, __не используйте `::` как разделитель путей,__ вместо этого используйте `_`.

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

Если файл необходимо удалить, сделайте это в цели `post-extract`, а не как часть исправления.

[[slow-patch-manual]]
=== Ручное создание патчей

[NOTE]
====
Ручное создание патчей обычно не требуется. Предпочтительным методом является автоматическая генерация патчей, как описано ранее в этом разделе. Однако иногда может потребоваться ручное исправление.
====

Патчи сохраняются в файлы с именами [.filename]#patch-*#, где * указывает на путь к файлу, который патчится, например [.filename]#patch-Imakefile# или [.filename]#patch-src-config.h#. Патчи с именами файлов, не начинающимися с [.filename]#patch-#, не будут применены автоматически.

После изменения файла используется man:diff[1] для записи различий между оригинальной и изменённой версиями. `-u` заставляет man:diff[1] выводить различия файлов в "унифицированном" формате (unified diffs), которые являются предпочтительным форматом.

[source, shell]
....
% diff -u file.orig file > patch-pathname-file
....

При создании патчей с новыми файлами, добавляемыми в репозиторий, используется параметр `-N`, который заставляет man:diff[1] трактовать несуществующие прежде файлы как если бы они существовали, но имели пустое содержимое:

[source, shell]
....
% diff -u -N newfile.orig newfile > patch-pathname-newfile
....

Использование опции рекурсии (`-r`) в man:diff[1] для создания патчей допустимо, но пожалуйста, проверяйте полученные патчи, чтобы убедиться в отсутствии ненужных данных. В частности, различия между резервными файлами, [.filename]##Makefile##, когда порт использует `Imake` или GNU `configure`, и т.д., являются избыточными и должны быть удалены. Если потребовалось отредактировать [.filename]#configure.in# и запустить `autoconf` для перегенерации `configure`, не включайте различия в `configure` (его объём часто достигает нескольких тысяч строк!). Вместо этого определите `USES=autoreconf` и возьмите различия для [.filename]#configure.in#.

[[slow-patch-automatic-replacements]]
=== Простая автоматическая замена

Простые замены могут быть выполнены напрямую из [.filename]#Makefile# порта, используя режим редактирования на месте утилиты man:sed[1]. Это полезно, когда изменения используют значение переменной:

[.programlisting]
....
post-patch:
	@${REINPLACE_CMD} -e 's|/usr/local|${PREFIX}|g' ${WRKSRC}/Makefile
....

[IMPORTANT]
====
Используйте man:sed[1] только для замены изменяемого содержимого. Для замены статического содержимого необходимо использовать файлы исправлений вместо man:sed[1].
====

Довольно часто портируемое программное обеспечение использует соглашение CR/LF в исходных файлах. Это может вызвать проблемы с дальнейшим наложением патчей, предупреждениями компилятора или выполнением скриптов (например, `/bin/sh^M не найден`). Для быстрого преобразования всех файлов из CR/LF в просто LF добавьте следующую запись в [.filename]#Makefile# порта:

[.programlisting]
....
USES=	dos2unix
....

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

[.programlisting]
....
USES=	dos2unix
DOS2UNIX_FILES=	util.c util.h
....

Используйте `DOS2UNIX_REGEX` для преобразования группы файлов во вложенных каталогах. Его аргумент — это совместимое с man:find[1] регулярное выражение. Подробнее о формате можно узнать в man:re_format[7]. Эта опция полезна для преобразования всех файлов с заданным расширением. Например, преобразовать все исходные файлы кода, оставив двоичные файлы без изменений:

[.programlisting]
....
USES=	dos2unix
DOS2UNIX_REGEX=	.*\.([ch]|cpp)
....

Аналогичной опцией является `DOS2UNIX_GLOB`, которая запускает `find` для каждого указанного в ней элемента.

[.programlisting]
....
USES=	dos2unix
DOS2UNIX_GLOB=	*.c *.cpp *.h
....

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

[.programlisting]
....
USES=	dos2unix
DOS2UNIX_WRKSRC=	${WRKDIR}
....

[[slow-patch-extra]]
=== Внесение исправлений при условии

Некоторые порты требуют патчей, которые применяются только для определённых версий FreeBSD или при включении или отключении конкретной опции. Условные патчи указываются путём размещения полных путей к файлам патчей в `EXTRA_PATCHES`. Имена файлов условных патчей обычно начинаются с [.filename]#extra-#, хотя это и не обязательно. Однако их имена _не должны_ начинаться с [.filename]#patch-#. Если это произойдёт, они будут применены безусловно фреймворком, что нежелательно для условных патчей.

[[slow-patch-extra-ex1]]
.Применение патча для конкретной версии FreeBSD
[example]
====
[.programlisting]
....
.include <bsd.port.options.mk>

# Patch in the iconv const qualifier before this
.if ${OPSYS} == FreeBSD && ${OSVERSION} < 1100069
EXTRA_PATCHES=	${PATCHDIR}/extra-patch-fbsd10
.endif

.include <bsd.port.mk>
....

====

[[slow-patch-extra-ex2]]
.Опциональное применение патча
[example]
====
Когда для crossref:makefiles[makefile-options,опции] требуется патч, используйте ``opt_EXTRA_PATCHES`` и ``opt_EXTRA_PATCHES_OFF``, чтобы сделать исправление зависимым от опции `opt`. Дополнительные сведения см. в crossref:makefiles[options-variables,"Замена дополнительных переменных, `OPT_VARIABLE` и `OPT_VARIABLE_OFF`"].

[.programlisting]
....
OPTIONS_DEFINE=	  FOO BAR
FOO_EXTRA_PATCHES=  ${PATCHDIR}/extra-patch-foo
BAR_EXTRA_PATCHES_OFF=	${PATCHDIR}/extra-patch-bar.c \
		${PATCHDIR}/extra-patch-bar.h
....

====

[[slow-patch-extra-ex-dirs]]
.Использование `EXTRA_PATCHES` с каталогом
[example]
====
Иногда для функции требуется множество патчей, в таком случае можно указать `EXTRA_PATCHES` на каталог, и все файлы с именем [.filename]#patch-*# в нём будут применены автоматически.

Создайте подкаталог в [.filename]#${PATCHDIR}# и переместите в него патчи. Например:

[source, shell]
....
% ls -l files/foo-patches
-rw-r--r--  1 root  wheel    350 Jan 16 01:27 patch-Makefile.in
-rw-r--r--  1 root  wheel   3084 Jan 18 15:37 patch-configure.ac
....

Затем добавьте это в [.filename]#Makefile#:

[.programlisting]
....
OPTIONS_DEFINE=	FOO
FOO_EXTRA_PATCHES=	${PATCHDIR}/foo-patches
....

Затем фреймворк использует все файлы с именем [.filename]#patch-*# в этом каталоге.
====

[[slow-configure]]
== Конфигурирование

Поместите все дополнительные команды, требуемые для настройки, в ваш скрипт [.filename]#configure# и сохраните его в подкаталоге [.filename]#scripts#. Как отмечено выше, вы можете сделать это целями в файле [.filename]#Makefile# и/или скриптами с именами [.filename]#pre-configure# или [.filename]#post-configure#.

[[slow-user-input]]
== Обработка пользовательского ввода

Если для построения, конфигурации или установки вашего порта требуется некоторый ввод со стороны пользователя, то вы должны задать переменную `IS_INTERACTIVE` в вашем файле [.filename]#Makefile#. В случае "ночного построения" это позволит пропустить ваш порт, если пользователь в своём окружении задал переменную `BATCH` (и если пользователь установил переменную `INTERACTIVE`, то будут строиться _только_ порты, которые требуют взаимодействия с пользователем. Это сэкономит значительное количество времени на части машин, которые постоянно строят порты (смотрите ниже).

При наличии разумных ответов на задаваемые вопросы, подходящих по умолчанию, также рекомендуется проверять переменную `PACKAGE_BUILDING` и выключать интерактивный скрипт, если он есть. Это позволит нам строить пакеты для помещения на компакт-диски и FTP-серверы.
