[FreeBSD] メガアップデート
ソフトウェア更新作業にはたまに膨大なパッケージの交換を要するときがあり、 そんなことを mega updates ともいうそうです*1。 最近 Unicode を扱う基幹ライブラリ ICU が更新され、 FreeBSD の port でも 4.6 版に改められました。 私のマシンに入れている 1172 個の ports のうちこの port に依存する ports は 370 ほどもありました。 貧弱なマシンにとって交換作業は荷が重すぎます。
devel/icu の更新はインストールするライブラリのファイル名を libicui18n.so.38 から libicui18n.so.46 に変えたので、 これに頼る ports のうち旧名で呼び起こそうとする依存プログラムはすべてビルドしなおし再インストールが必要です。 *2 しかしなかにはアイコンファイルを入れるだけのパッケージなどもあり、 それらは更新の必要がありません。 またライブラリ依存しているプログラムのなかには libicui18n.so に依存となっているものもあり、 これらの ports は更新が必要ではありませんでした。 これらを除外すればかなり手数を減らせるはずです。
作業の手順
更新の必要があるかどうかを判定するには ELF バイナリファイルを調べて名前が決め打ちになっているものを探し、 そのファイルをインストールしている ports だけを更新すればよいと考えました。 判定には file コマンドと ldd コマンドを利用します。
FreeBSD の ports 管理者が提供する自動化スクリプトもうまく使えばこの作業を軽減できるはずなのですが、 マシンが貧弱なため利用を諦めました。 たとえば、 毎日 ports の情報を更新するたびに走らせている portversion コマンドはデータベースを更新するのにかなり時間を費します。 インストールした ports の数が多すぎるためなのかもしれません。 portupgrade のような自動化スクリプトも同様に各 port の更新のたびにデータベースを書き換えるので負担が大き過ぎ、 時間がいくらあっても足りません。
したがって手作業で順に更新をするので、 自作したスクリプト で約 370 の ports の依存関係を示す表を作ります。 INDEX ファイルに似ています。 これはその抜粋です。
... libgnomecups-0.2.3_4,1||print/libgnomecups| libgnomeprint-2.18.8||print/libgnomeprint| xfce4-print-4.6.1_6||print/xfce4-print| libxfce4gui-4.6.4 libxfce4util-4.6.2 xfce4-conf-4.6.2 dirmngr-1.1.0_3|1.1.0_1|security/dirmngr| libksba-1.1.0 gnupg-2.0.16_4|2.0.16_2|security/gnupg| libksba-1.1.0 gpgme-1.3.0_1|1.3.0|security/gpgme| gnupg-2.0.16_4 libksba-1.1.0 libksba-1.1.0|1.0.8|security/libksba| brasero-2.32.1||sysutils/brasero| desktop-file-utils-0.15_2 gnome-desktop-2.32.1 nautilus-2.32.2 totem-pl-parser-2.32.1 lxterminal-0.1.9||sysutils/lxterminal| thunar-volman-0.3.80_2||sysutils/thunar-volman-plugin| Thunar-1.0.2 desktop-file-utils-0.15_2 libexo-0.3.107 libxfce4gui-4.6.4 libxfce4util-4.6.2 xfce4-conf-4.6.2 xfce4-panel-4.6.4 ...
"|" 記号で区切られた第 1 項は port 名、 第 2 項は旧版番号、 第 3 項は port のパス、 最後の第 4 項が先に更新が必要な依存先の ports です。 たとえば /usr/ports/security/libksba に入って libksba-1.0.8 を libksba-1.1.0 に更新すれば、 そのあとに /usr/ports/security/gnupg に移って gnupg-2.0.16_2 が gnupg-2.0.16_4 に更新できるというわけです。 また lxterminal-0.1.9 は版番号を改めないまま再インストールが必要かもしれないという意味です。
更新を行なったらついでに /var/db/pkg/ 以下に記録されている ports のうち今回更新した port に依存している port を検索し、 その +CONTENTS ファイルに記されたバージョン情報を、 旧版番号から新版番号に改めておきます。 こうすると pkgdb -Fi の時間が短縮できるのです。 更新後は port 名をこの表から削除してゆきます。 第 4 項が空になった ports をつぎに更新することになります。 どんどん表が小さくなってゆくのを見るとかなり満足できます。
さて今回は版番号を改めない ports のうち再ビルドが必要ないものを判定するつもりでいます。 上の表では第 2 項が空のものを調べます。 つぎのようなスクリプトで更新が必要かどうかを決めました。
#! /bin/sh usage() { cat <<EOF Usage: `basename $0` [OPTION] [PORTNAME] Option: [-v|--verbose] : show results of \`ldd -a\` Portname: It should be a full name. If you give a portion, this script gives some suggestion. EOF exit $1 } if test $# -eq 0; then usage 1 1>&2 fi verbose=false while test $# -gt 1; do case "$1" in -v|--verbose) verbose=true ;; esac shift done dir=$1 contfile=/var/db/pkg/${dir}/+CONTENTS if [ ! -e ${contfile} ]; then candidate=`(cd /var/db/pkg/; ls -d ${dir}*)` if [ "" != "${candidate}" ]; then echo "Which port?: " ${candidate} fi exit fi base=`head ${contfile} | grep @cwd | sed 's,@cwd ,,'` for item_file in `grep -v @ ${contfile}` do if [ "" != "`file ${base}/${item_file} | grep ELF`" ]; then blame=`ldd ${base}/${item_file} | grep "not found" | sort | uniq` if [ "" != "${blame}" ]; then if [ "${verbose}" = "true" ]; then printf "########## ${item_file}\n" #printf "\e[36m%s\e[0m\n" "${item_file}" #printf '(\[$(tput md)\]%s \[$(tput me)\])\n' "${item_file}" ldd -a ${base}/${item_file} | grep -E "^/usr/|not found" \ | grep -B1 "not found" else echo ${item_file}:${blame} fi fi fi; done
要するにこのスクリプトは、 インストールされているファイルに ELF バイナリがあれば ldd コマンドで中を見て、 依存関係が壊れているものを表示します。 依存関係に何も問題なければ上の表からその port 名を除去してゆきます。