Рашид Ачилов
Создаем порт для FreeBSD своими руками
Часть I: основные возможности
Автоматизированная система сборки стороннего программного обеспечения из исходных текстов (система портов) - это то, чем по праву гордится FreeBSD. Система содержит ссылки на десятки тысяч программ, и этот список постоянно пополняется. Кто их создает - эти пополнения - некие выдающиеся специалисты? Да вовсе нет. Вы тоже сможете стать одним из них.
Рашид Ачилов
Споры о том, что правильнее - собирать программы руками или использовать для этого порты, в эхо-конференции FidoNet RU.UNIX.BSD не утихают никогда. Они могут стихнуть на время, но затем кто-нибудь снова задаст такой вопрос, и они разгораются с новой силой.
И рано или поздно каждый приходит к мнению, что использовать порты удобнее, потому что:
• Система сама ведет список файлов, входящих в порт, что позволяет просто удалить программу, не заботясь о том, что на диске останутся «хвосты».
• Система сохраняет контрольную сумму MD5 по каждому файлу, что позволяет проверить его подлинность в случае возникновения сомнения, а также при удале нии программы. Поскольку при удалении порта проверяются контрольные суммы файлов, его составляющих, система не удалит файл, если его контрольная сумма не совпадает с записанной. Это исключает ситуацию, когда тщательно настроенный, но как обычно нигде не зарезервированный конфигурационный файл программы пропадает при ее удалении.
• Система отслеживает выход новых версий программы (это верно только для портов, которые сопровождаются кем-то еще, а не вами. О своевременном обновлении вашего порта вы должны заботиться сами).
• Значительно проще ответить на вопрос «Установлена ли у меня эта программа?» или «Где у меня такой-то файл от такой-то программы?»
Основные моменты того, как устроена система портов и как с ней эффективно работать, изложены в [1], здесь я приведу только ее краткое описание, необходимое для понимания статьи. Система портов или «коллекция портов» представляет из себя древовидную структуру каталогов, которая обычно размещается в /usr/ports. Имена каталогов первого уровня (находящиеся непосредственно в /usr/ports) образуют наименования «категорий», то есть тематических групп. Например, есть категория mail, в которую входят все программы, связанные с обработкой электронной почты, есть категория dns и т. д. Решение о создании новой категории принимает FreeBSD Team, которая создает на первичных зеркалах новый каталог, переносит в него нужные порты, откуда изменения расходятся по вторичным зеркалам и обычным серверам. Перед тем как приступить к созданию порта, необходимо решить, к какой категории относится программа, которую предстоит внести в дерево портов. Имена категорий и их краткое описание приведено в [2]. Внутри каталога категории размещаются непосредственно порты - по одному в каждом отдельном каталоге. Такая структура несколько замедляет навигацию, когда в каталоге находится много файлов, и именно поэтому постоянно появляются новые и новые категории.
Из чего, собственно, состоит порт для FreeBSD?
Порт для FreeBSD состоит из нескольких файлов, которые сами по себе ничего не делают. Даже несмотря на то, что один из них называется Makefile, все они представляют из себя файлы данных - описания и определения некоторых переменных, используемых системой портов в качестве исходных данных. Механизм работы системы давным-давно отлажен, в простейшем случае необходимо только задать исходные данные. Ниже приводится список файлов, которые необходимы для порта (на самом деле порт может использовать и другие файлы, но они не являются необходимыми):
• Makefile - основной файл порта. В нем задаются все необходимые переменные, которые настраивают работу системы портов в том или ином режиме. Также в этом файле может программироваться собственно поведение системы в той или иной ситуации. Текстовый файл, стандартного формата, распознаваемого программой BSD make (не путать с GNU make! О синтаксисе файлов BSD make см. man make). Данный файл условно делится на четрые секции, которые должны идти строго друг за другом, не нарушая порядка. Определения переменных, которые должны появляться в данной секции, не должны появляться ни в предыдущей, ни в последующей секции. Порядок секций таков: начальная секция -> секция MAINTAINER -> секция USE_* -> секция доопределений.
• Начальная секция идет самой первой. В нее помещаются переменные PORTNAME, PORTVERSION, CATEGORIES, MASTER_SITES и DISTNAME. Также в нее могут быть помещены другие переменные, полный перечень которых приведен в файле bsd.port.mk.
• Секция MAINTAINER идет непосредственно после начальной секции. Как правило, она содержит только две переменные - MAINTAINER и COMMENT. Смысл этой секции в том, что она определяет персону, отвечающую за данный порт, и дает краткое описание этого порта. В целях безопасности все запросы на патчи (да и сами патчи тоже), пришедшие для уже принятого порта не с адреса, указанного в поле MAINTAINER, а с другого адреса, перенаправляются MAINTAINER на утверждение и вносятся в порт только в том случае, если будут им одобрены.
• Секция USE_* расположена после секции MAINTAINER. Она содержит различные переменные, задающие явно или неявно зависимости для данного порта. Например, «USE_GETTEXT=yes» задаст зависимость порта от devel\gettext, «USE_KDEBASE_VER=3» задаст зави симость от x11\kdebase3 и т. д. Список всех возможных переменных USE_* приведен в файле bsd.port.mk. Может отсутствовать, но, как правило, присутствует - трудно найти порт, который вообще ни от чего не зависит.
• Секция доопределений - последняя секция в файле. Может отсутствовать и зачастую так и происходит. Содержит переопределения различных мишеней, составляющих процесс сборки порта. При сборке порта последовательно выполняются мишени fetch, extract, configure, build и install. Каждая из них в свою очередь состоит из трех этапов: pre-sometarget, do-sometarget и post-sometarget. Эти этапы предназначены для проведения дополнительной предварительной или после дующей обработки, а также для замещения некоторого этапа, если в этом есть необходимость. Например, этап do-extract может быть переопределен для выполнения распаковки дистрибутива нестандартным образом. Существуют и другие этапы, полный их перечень приведен в файле bsd.port.mk.
• distinfo - файл, описывающий все файлы, входящие в дистрибутив программы. Его нет необходимости создавать вручную - после создания Makefile достаточно выполнить команду make makesum - и он будет создан автоматически.
• pkg-descr - текстовый файл с небольшим описанием программы и ее возможностей. Описание может быть вообще коротким - 2-3 строки или более подробным 10-15 строк. В конце файла желательно привести URL разработчика и его адрес электронной почты в формате:
WWW: http://this.insert.link/~homepage
Author: John J. Smith jsmit@someserver.tld
• pkg-plist - список всех файлов, входящих в программу, а также команд, выполняемых при установке и/или удалении данной программы. Файлы указываются с путями относительно каталога установ ки (по умолчанию /usr/local), могут содержать макросы (будут рассмотрены ниже). Команды задаются в формате, описанном в man pkg_create.
• Другие файлы с произвольными именами. Например, файл сообщения, выдаваемого после успешной установки (или удаления) обычно называется pkg-message, хотя конечно никто не помешает назвать его как угодно по-другому.
Итак, что понадобится для того, чтобы приступить к созданию своего собственного порта?
• Дистрибутив программы. Его следует поместить в /usr/ports/distfiles - это обычно место, куда скачиваются дистрибутивы и где система портов будет их искать.
• Программа должна собираться и устанавливаться без ошибок. Если создается порт для чужой программы и она собирается с ошибками, следует найти причину, но не править исходный текст, а подготовить патч (если только не предполагается выкладывать исправленный дистрибутив на собственном сервере).
• Решение о присвоении программе определенной категории.
• Любимый текстовый редактор.
• Терпение и свободное время - возможно, порт придется пересобирать не один раз и даже не десять.
Первый порт - несложная программа для КDЕ
Мы не будем заниматься искуственными примерами. В качестве примера первого порта возьмем несложную программу для KDE, взятую с сайта http://www.kde-apps.org. Почему именно для KDE? Сообщество разработчиков KDE огромно, программы появляются, развиваются, а потом перестают поддерживаться и развиваться постоянно, причем большинство их авторов из Европы, работающие в той или иной версии Linux и даже и не подозревающие, что их программа может работать и в другой операционной системе, и ситуация, когда на KDE applications находится интересная и нужная программа, - это совершенно обычная ситуация. Итак, в качестве примера возьмем программу, которая позволяет встроить в панель KDE выпадающее меню с содержимым адресной книги. Программа так и называется - «Contacts menu for Kicker». Подробное ее описание приведено в [3]. Факт успешной сборки программы уже проверен, программа установлена, и протокол установки сохранен в файле, то есть была выполнена команда:
# make install >& install.log
В данном примере для перенаправления в файл и обьединения выводов stdout и stderr используется синтаксис tcsh, в sh необходимо выполнить:
# make install > install.log 2» install.log
Начинаем. Сразу же необходимо иметь в виду, что существует довольно жесткий порядок, в котором в файле Makefile должны появляться определения переменных. В нижеследующем примере приводится правильный порядок и нарушать его не рекомендуется.
Файл Makefile
В соответствии с рекомендациями [4] Makefile должен иметь следующий заголовок:
# New ports collection makefile for: contactsmenu
# Date created: 01 Mar 2006
# Whom: Rashid N. Achilov shelton@granch.ru
#
# $FreeBSD$
На этом заголовок кончается.
Внимание! Для впервые отправляемого порта строка $FreeBSD$ должна выглядеть именно так, как показана!
Первыми строками, идущими за заголовком, должны быть следующие:
PORTNAME= contactsmenu
PORTVERSION= 0.3.4b
CATEGORIES= mail kde
Эти три переменные должны идти первыми и именно в том порядке, в котором они приведены. Первая из них задает имя порта. Она должна совпадать с именем каталога с файлами порта. Вторая задает номер текущей версии программы. Именно по ней будет проводится сравнение существующей и установленной версий. Третья перечисляет список категорий, к которым относится данный порт. Выбор категории, а также требования к составлению данного списка приведены в [2].
MASTER SITES= http://www.kde-apps.org/content/files/
Эта переменная должна идти сразу после CATEGORIES. Она задает список сайтов, с которых будет скачиваться дистрибутив программы.
Откуда взять имя дистрибутива и адрес домашнего сайта проекта? Как правило, первоначальная закачка файла производится вручную, следовательно, имя файла и URL исходного сайта всегда можно найти в логах программ, которыми он закачивался. Можно использовать для этого и другие методы, которые я не буду здесь описывать ввиду их чрезвычайно большого разнообразия. Таким образом, если бы имя дистрибутивного файла нашей программы было contactsmenu-0.3.4b.tar.gz, нам бы больше ничего не потребовалось указывать - вся информация для загрузки уже предоставлена. Но не все так просто, потому что имя нашего файла - 34479-contactsmenu-0.3.4b.tar.bz2.
Что делать? Для таких случаев предусмотрено принудительное задание имени дистрибутивного файла, которое должно быть полным, то есть
DISTNAME= 34479-${PORTNAME}-${PORTVERSION}
Включив в нижеследующие секции «USE_BZIP2=YES» мы сформировали полное имя дистрибутивного файла.
Для многих популярных URL типа www.apahe.org, sourceforge.net, www.kde.org и пр. определены специальные макросы, в которых перечислены все URL, на которых можно найти данную программу. Например, если бы данная программа располагалась на сайте sourceforge.net, то строка MASTER_SITES была бы заменена следующей комбинацией:
MASTER_SITES= ${MASTER_SITE_SOURCEFORGE}
MASTER SITE SUBDIR= contactsmenu
что означало бы загрузку файла с сайтов, входящих в заранее определенный список из подкаталога contactsmenu.
MAINTAINER= shelton@granch.ru
COMMENT= KDE 3.x addressbook Kicker applet
Эти строки должны идти в том порядке, в котором приведены. MAINTAINER задает адрес электронной почты лица, которое создало и управляет данным портом. COMMENT содержит краткое (одну строчку) описание данного порта.
Внимание! При использовании нескольких адресов электронной почты в поле MAINTAINER должен быть проставлен тот адрес, который будет указан в поле From: во время отправки подготовленных файлов порта командой send-pr. Если в поле MAINTAINER будет указан один адрес, а обновления порта пойдут с другого адреса, придется дополнительно подтверждать, что данное письмо отправлено именно майнтайнером порта, а не является подделкой.
USE_KDEBASE_VER= 3
USE_GMAKE= yes
USE BZIP2= yes
Начинается секция переменных USE_*. Здесь, как правило, перечисляются неявные зависимости, заранее определенные в системе. USE_KDEBASE задает зависимость порта от пакета kdebase3, USE_GMAKE - от пакета gmake, USE_BZIP2 - от пакета bzip2 (и заодно устанавливает EXTRACT_SUFX в «.tar.bz2»).
Что означает «порт X зависит от порта Y»? Это означает, что в соответствии с тем, к какому типу будет отнесена данная зависимость (EXTRACT_DEPENDS, RUN_DEPENDS и т. д., см. bsd.port.mk для полного списка), то на данном этапе построения порта (extract, install и т. д.) система проверит наличие установленного пакета, который указан как зависимость, и если он не установлен, система автоматически перейдет к его установке. В этом проявляется еще одно преимущество системы портов - имея скоростной канал в Интернете и дешевый трафик, можно не думать, например, о том, какие файлы нужны для установки KDE - достаточно перейти в каталог x11/kde и набрать make. Построение правильного списка зависимостей - одна из задач автора порта. Если указать ненужные программы - порт будет пытаться их поставить, что будет забивать систему мусором, если же забыть нужные - порт в лучшем случае не соберется, в худшем - соберется и не будет работать.
GNU_CONFIGURE= yes
CONFIGURE_ARGS += --with-qt-dir=${QT_PREFIX} \
--with-extra-includes=${LOCALBASE}/include \
--with-extra-libs=${LOCALBASE}/lib
Эти строки должны присутствовать (если они есть) после всех переменных USE_*. Они определяют, что для создания Makefile, управляющего сборкой программы, будет использоваться configure, и задают дополнительные аргументы, передаваемые при вызове configure. При сборке программы configure получает неявные параметры, задаваемые, например, с помощью PREFIX, но может получать и явные параметры, перечисляемые выше.
Ну и последней строкой нашего Makefile обязательно должна быть:
.include <bsd.port.mk>
которая, собственно, и загрузит основной файл. Вот и все, файл, управляющий сборкой программы создан.