Portupgrade: коррекция системы портов FreeBSD

Алексей Федорчук
23 августа 2005 г

Система портов обладает множеством неоспоримых достоинств и в своем первозданном виде. Силою тройного волшебного заклинания

$ make install clean

легко установить «с нуля» любую. программу (при наличии сети и толики времени, конечно).

Чуть похуже она проявляет себя при обновлении пакета, требуя еще двух пар страшных заклятий — make deinstall и make reinstall. Что, впрочем, не столь уж обременительно — если речь идет о единичном порте. А вот если требуется обновить целый программный комплекс, подобный KDE, да еще имеющий сложные и неоднозначные зависимости, — тут становится немножечко скучно. Не под впечатлением ли подобных процедур родились идеи о прикручивании к FreeBSD инфраструктуры Debian’овского apt или портежей Gentoo?

Однако, как известно, спасение утопающих — дело рук самих утопающих. И резервы кардинального усовершенствования системы портов лежат в них же самих. А именно — внутри порта sysutils/portupgrade/, разработанного и поддерживаемого Akinori Musha. Каковой и призван облегчить жизнь пользователя, время от времени обновляющего большие пакетные комплексы или даже все установленные порты оптом.

Что такое portupgrade? Во-первых, это система учета и контроля, то есть базы данных портов, наличествующих в дереве этой системы, и пакетов, установленных на конкретной машине. Они основаны на механизме B-дерева (Btree), существуют в бинарном виде, и потому работа с ними происходит более быстро, чем со стандартными базами портов FreeBSD, имеющими текстовый формат.

Во-вторых, portupgrade включает комплекс утилит для автоматизации установки и удаления портов и пакетов и обновления их баз данных, получения информации об обновившихся версиях, и так далее.

Чтобы воспользоваться предлагаемыми portupgrade возможностями, ее для начала следует установить. Что можно сделать как из прекомпилированных бинарников, так и из портов. В первом случае (при наличии подключения к сети) даем команду

$ pkg_add -r portupgrade

во-втором — прибегаем к помощи все тех же заклинаний:

$ cd /usr/ports/sysutils/portupgrade
$ make install clean

Все утилиты пакета portupgrade — это сценарии, написанные на языке Ruby (также имеющем японское происхождение — его создатель Юкихиро Мацумото). И потому в обоих случаях, кроме самого portupgrade, будет установлен также интерпретатор этого языка (с некоторыми дополнительными файлами) и библиотека gdbm, используемая для построения баз данных. Поэтому процесс сборки из портов может несколько затянуться…

После установки portupgrade неплохо выполнить обновление дерева портов. До сего времени стандартным средством для этого (если не качать весь тарбалл, конечно), был cvsup. Однако сейчас в базовый комплект текущей ветки FreeBSD включена специальная утилита — portsnap, ранее доступная в качестве отдельного порта (sysutils/portsnap/).

Приведя тем или иным образом дерево портов в актуальное состояние, его (то есть состояние) следует зафиксировать в базе данных воспринимаемой portupgrade. Для чего прибегаем к первой из утилит этого порта — portsdb. Данная в форме

$ portsdb -Uu

она, во-первых, создаст требуемую нам базу данных — /usr/ports/INDEX.db, имеющую бинарный формат (за это отвечает опция -u). Кроме того, она обновит и стандартную базу данных системы портов — файл /usr/ports/INDEX (это обеспечивает опция -U). Или, если таковая ранее не создавалась (это делается стандартной командой make index), то сгенерирует ее заново — на предмет использования штатными средствами типа make search. Кстати, создание базы INDEX.db — дело достаточно длинное (так что следует заранее запастись терпением), но далеко не столь длинное, как выполнение операции make index

Теперь можно проверить соответствие установленных пакетов (вне зависимости от того, ставились ли они из бинарников или собирались из портов) текущему состоянию. Этой цели служит следующая утилита комплекта — portversion, вызываемая в такой форме:

$ portversion -l "="

где опция -l предписывает сверку установленных пакетов с актуальной базой данных по критерию, задаваемому ее значением. Легко догадаться, что приведенное значение (вспоминаем из арифметики символ «меньше») предписывает отыскать в системе пакеты, более древние, чем фигурируют в текущем дереве портов. И результирующий список будет тем изобильнее, чем больше времени прошло с развертывания портов или пакетов в системе.

Утилита portversion выполняет сравнение базы данных портов вообще с базой установленных в системе пакетов. Но не той стандартной, образованной содержимым каталога /var/db/pkg/, а с новосозданной /var/db/pkg/pkgdb.db. В принципе она создается при первом запуске portversion, а в дальнейшем обновляется при каждом использовании утилит portupgrade. Однако не исключено, что иногда потребуется и ручное обновление установленной базы (правда, мне ни разу не понадобилось, но все же…). Для этого предназначена очередная утилита — pkgdb, которую можно запускать в интерактивном (с опцией -F) или автоматическом (-fu) режиме.

Теперь можно заняться и тем делом, ради которого всю эту бодягу затеяли — тотальным обновлением всего хозяйства, установленного помимо базовой системы (которая, как мы помним, обновляется посредством make buildworld/buildkernel и make installkernel/installworld). Для этого предназначена центральная утилита порта — portupgrade, она же portinstall. Имена эти суть хардлинки на один и тот же исполняемый файл, в чем легко убедиться командной

$ ls -i1 /usr/local/sbin/{portupgrade,portinstall}

из вывода которой явствует, что оба файла имеют один и тот же идентификатор:

1212832 /usr/local/sbin/portinstall*
1212832 /usr/local/sbin/portupgrade*

Как следует из man portinstall, команда эта имеет множество опций, из которых практически важнейшими для нас являются следующие:

  • -a — обновление всех портов, для которых portversion выявила «старение» по сравнению с текущим деревом;
  • -r — рекурсивное обновление всех портов, которые зависят установленных и обновляемых;
  • -R — рекурсивное обновление всех портов, от которых зависят пакеты, установленные в системе.

Две последние опции могут показаться тавтологичными, но это не так: только их сочетание позволяет корректно обновить все установленные ранее пакеты (да и то не всегда — в некоторых случаях может потребоваться ручное вмешательство). Так что наиболее употребимой формой использования нашей утилиты будет такая:

$ portupgrade -arR

После чего остается только ждать: этой командой будут скачаны исходники всех портов, нуждающихся в обновлении, распакованы, скомпилированы и инсталлированы в порядке, определяемом зависимостями, примерно также, как это происходило бы при последовательной отдаче директив make install clean для каждого нужного порта. Что, понятное дело, может занять немалое время.

При dial-up соединении это будет выглядеть очень скучно: утилита portupgrade будет то и дело лезть в сеть за исходниками, потом собирать очередной порт, затем снова скачивать исходники следующего, и так далее. Поэтому имеет смысл (и не только на модеме) предварительно скачать из сети все потребное, что предписывается опцией -F (или --fetch-only):

$ portupgrade -arRF

И теперь можно засечь время чистого «компилежа»: для основных пакетов KDE (все нужное, без kdetoys, kdeedu и лишних локализаций), у меня на машине с Athlon64 оно заняло более 10 часов.

Впрочем, и это — не совсем чистое время. Время от времени следует обращаться к консоли, в которой выполняется portupgrade: многие порты в ходе своей установки требуют интерактивного ответа на всякие вопросы (типа — нужна ли поддержка сканирования или печати в KDE). И в ожидании таковых всякая жизнедеятельность portupgrade прекращается.

Я уже говорил, что все бывает не совсем так пушисто и шоколадно, как в теории. В частности, выполняя обновление KDE с 3.4.1 до 3.4.2, я столкнулся с интересным фактом: обновив зависимости, portupgrade очень долго собирала порт kdelibs, после чего выдала ошибку. Суть коей можно было выяснить, только пролистав назад пяток экранных буферов: оказалось, что устанавливаемый порт конфликтует с ранее имеющимся kdeadmin, который предлагалось удалить вручную. Так что, ребята, внимательно читайте сообщения об ошибках: разумного, доброго, вечного в них не меньше, чем у тети Мани.

В принципе, для разрешения (в том числе и) таких коллизий предназначена опция -f (--force), которая обеспечивает принудительную пересборку всех ранее установленных портов, в том числе и не устаревших. Необходимость в ней возникает, кроме того, при переходе от ветки к ветке: ибо нет гарантий, что все порты, собранные, например, для FreeBSD 5.X, будут корректно работать во FreeBSD 6.

Важный момент: в ходе сборки портов посредством portupgrade очистка дерева портов (то есть процедура make clean) выполняется как до, так и после сборки каждого порта, что немало способствует экономии дискового пространства в текущий момент времени. Тем не менее, при желании покопаться в исходниках это можно запретить. На сей предмет существуют опции -w (--noclean) и -W (--nocleanup), первая из них (запрет очистки до сборки) может использоваться и сама по себе, несколько сокращая суммарное время обновления портов.

Кроме тотального обновления установленных портов, утилита portupgrade может применяться также для установки или обновления единичного порта — имя его следует указать в качестве аргумента. А опция -r обеспечит также сборку всех портов, от которых зависит данный.

Наконец, portupgrade может использовать для обновления и репозиторий бинарных пакетов. Это обеспечивается опцией -P, требующей указания имени порта в качестве аргумента. Таким образом можно выполнить и тотальное обновление пакетов — для этого, вместо имени порта, потребуется задать опцию -a; в сочетании же с опцией -R будет выполнено рекурсивное обновление для всех зависимостей установленных портов.