MBPのApple EFIが自動的に認識するブートEFIファイルについて

この記事はUEFI Advent Calendar 2014 - Qiitaの第9日目として書かれました。
以下の内容はMacBook Pro Retina 13inch (late 2013)を使って検証しました。おそらく、最近のMacBook Pro/Airであれば同様の結果になると思われます。
私はMBP買ったらすぐにGentoo Linuxでシングルブートするマンであり、OS Xに関する部分は以前のメモに頼っている部分もあるため、検証が甘いと思われます(いちおう、SDカード上にOS X Yosemiteを入れて確かめています)。

MacBook Pro(以下MBP)の起動時にジャーンと鳴っている間に[Option]キーを押しておくと、グラフィカルなメニューが表示され、起動させるOSを選択することができます。この起動メニューはApple的にはStartup Managerと言うらしいですね。このStartup Managerに表示される項目について調べました。

Startup Managerに表示される条件

Startup Managerにエントリが表示される条件は、内蔵SSDUSBメモリ・SDカードを問わず、以下の条件を満たすファイルが存在することです。

パーティションテーブルはGPT、MBRパーティションテーブルのいずれでも可です。

上記のEFI起動ファイルが存在していればStartup Managerの起動エントリとして表示されます。複数のファイルが存在していれば優先順位は上記の順番通りです。

更に、ディスクがMBRパーティションテーブルである場合、ブートセクタが起動可能か否かにかかわらず、Windowsというラベルでエントリが表示されます。

このチェックは全てのディスク上のHFS+またはvfatパーティションに対して行われます。GPTのパーティション数は一般に最大128ですので、とてもたくさんのエントリを表示させることができますね(実際に幾つまで表示されるかは確認していません)。

外付け光学ディスクドライブの手持ちが無かったのでそれに関しては検証していません。ごめんなさい。
また、BootCampも使ったことが無いので全くわかりません。ごめんなさい。

Startup Managerの表示について

エントリのアイコンは対象パーティションのルートに/.VolumeIcon.icnsというMacアイコンファイルが存在すれば、それが使われます。GNU/Linuxではpng2icnsというコマンドでMacアイコンファイルを作成できます(Gentooではmedia-libs/libicnsに含まれます)。無ければデバイスに応じたデフォルトアイコンになります。

アイコンの下に表示されるテキストラベルは、HFS+上のブートファイルに対してOSXのbless(8)コマンドのFolder Modeで--labelオプションを指定した場合、その文字列が使われます。ラベルの本体は"blessed"ディレクトリ下にある._disk_label, .disk_label_2xという謎フォーマットのファイルで、--labelで指定したテキストをフォントでレンダリングした謎フォーマットの画像ファイルと思われます。
blessしていない場合またはvfatファイルシステムの場合(当然vfat上のファイルはblessできません)は、"EFI Boot"というラベルになります。

まとめ

ここまででわかる通り、Startup ManagerはEFI変数のBoot Entryとは無関係です。また、当然ながらOSXのエントリはStartup Managerに表示されることが保証されています。OSX 10.9(Marverics)のブートファイルはOSXインストールHFS+上の/System/Library/CoreServices/boot.efiであり、OSX 10.10(Yosemite)のブートファイルはOSX復元パーティションのHFS+上の同じパスにあり、StartupManagerに表示されます。

EFI Stub KernelまたはGNU grubやrEFIndなどの汎用ブートローダ、はたまたEFI Shellなどをインストールする場合、上記のデフォルトエントリに置くか、HFS+上に置いてblessするだけで、Startup Managerから起動できます。EFI変数(NVRAM)を変更する必要すらありません(blessコマンドは--setBootオプションを指定しない限りNVRAMを変更しません)。これはEFI非対応の環境からLinux等をインストールする場合にも便利です。

私がどう利用しているかというと、EFI Stub KernelのBoot Entryを作り、BootOrderの先頭に設定して(vfat上に置いていますがデフォルトパスではないのでStartup Managerからは選択できない)普段はこれを起動しつつ、更に小さなvfatパーティションを2つ用意してそれぞれのEFIデフォルトブートパスにGNU grubEFI Shellを置いてStartup Managerから選択できるようにしてあります。
EFI Shellに(rEFIndパッケージから持ってきた)ファイルシステムドライバを読み込ませることで、EFI ShellからExt4やbtrfsにアクセスできるのは大変便利です。

おまけ

BootOrderの設定されていない状態では何が起動するか。
efibootmanager --delete-bootorderしてBootOrderを空にしてからMBPを起動させると、単純にSSD上の先頭パーティションから順に検索して起動可能なEFIファイルを起動します。この際、自動的に、発見されたEFI起動ファイルをEFI変数のBoot EntryとしてBoot0080とBootFFFFに保存し、更にBootOrderにBoot0080を設定します。
この挙動の通り、購入直後のMBPにはOSXのboot.efiを指すBoot0080とBootFFFFが登録されていて、BootOrderも0080が設定されていました。

systemdのこと

systemd使ってますか

Gentoo Linuxでもsystemdを使う人が増えてきた今日このごろ、皆さんもsystemdしているでしょうか。私はしていません。でもVirtualBox上のGentoo Linuxではしばらく前からsystemdを使っています。私は根っからのデスクトップユーザにしてアプリケーションユーザなので、技術的な考察などは守備範囲外なのですが、取り敢えずOpenRCとsysytemdとの違いとしてわかりやすい、起動時間の差を動画にしてみました。

ちょっと動画について補足説明します。
OpenRCはRC_PARALLEL="yes"を設定しています。
OpenRCで/etc/init.d/net.*ではなく/etc/init.d/dhcpcdを実行させています。こうすればdhcpcdがアドレス割り当てをバックグラウンドで待ちますので、若干他のサービス起動が速くなる、かもしれない。ricer的ですね……。
OpenRCと条件を揃えるために、systemdでもsyslog-ng, vixie-cron, ConsoleKitのサービスを起動しています。ですが、systemdではこれらのサービスが提供する機能をsystemdビルトイン機能で概ね置き換えることができます。

  • syslog -> systemd-journald
  • cron -> Timer unit
  • ConsoleKit -> systemd-logind

ということでsystemdではこれらのServiceを起動しなくても良いのですが、実は起動してもしなくても起動所要時間がほとんど変わらない(!!)ということがわかりました。systemd-analyzeによると、これら3つのサービスを起動した場合のuserspace起動時間は3309ms、起動しない場合は3005msとなって、0.3秒しか変化はありませんでした。

syslog-ngはin tree ebuildでインストールされるunitファイルが使えないので、野良リポジトリから新しいバージョンを入れています。
vixie-cronはunitファイルをインストールしないので、unitは自分で用意します。書くのは簡単ですが、このへんにも色々あります。

これからどうするの

日常的に使ってるGentoo Linux環境のsystemdへの本格的な移行は、今の所考えていませんが、いずれは移行しなきゃならないのかなあ。udevがsystemdが無いと動かなくなったら仕方ないですね。当面、それよりも先に問題になりそうなのはsystemd-logind関係で、現状既にConsoleKitはUpstream Dead、upowerもpm-utilsサポートをドロップしてsystemdの機能を使うことになりそうです。でもUbuntuがsystemd-logindを移植するような話が出ているので、どうなることやら。

HALが終わってデスクトップLinuxがpolkit/ConsoleKit/udisks/upowerに移行したのってたった3年くらい前の話なんですよね。もう3年経ったと言うべきなのか?

それはさておき

こんな動画を上げておいてなんですが、私はsystemdよりもOpenRCの方が好きです:-) 起動時間なんてそんなに重要じゃないし……(そもそもsystemdの売りは起動時間だけじゃないですが)。
でもGentoo is about choice.
systemd使いたければ使えばいいじゃない。Gentooでは両方使えるんだから。

KBUILD_OUTPUT環境変数: Linuxカーネルは一般ユーザでビルドしよう

最近になってKBUILD_OUTPUTという環境変数を知ったので、そのことについて書きます*1

Linux Kernelを一般ユーザでビルドしたい。

パッケージマネージャを利用してLinux Kernelをインストールすると、/usr/src以下に配置されます。パーミッションはroot:rootなので、当然一般ユーザではビルドできません。

$ make menuconfig
  HOSTCC  scripts/basic/fixdep
scripts/basic/fixdep.c:433:1: fatal error: opening dependency file scripts/basic/.fixdep.d: 許可がありません
コンパイルが中断されました。
make[1]: *** [scripts/basic/fixdep] エラー 1
make: *** [scripts_basic] エラー 2

当然ながら、カーネルビルド時のオブジェクトファイル群を別のディレクトリに生成させるオプションが用意されています。以下はLinux KernelのMakefileから該当部分。

# kbuild supports saving output files in a separate directory.
# To locate output files in a separate directory two syntaxes are supported.
# In both cases the working directory must be the root of the kernel src.
# 1) O=
# Use "make O=dir/to/store/output/files/"
#
# 2) Set KBUILD_OUTPUT
# Set the environment variable KBUILD_OUTPUT to point to the directory
# where the output files shall be placed.
# export KBUILD_OUTPUT=dir/to/store/output/files/
# make
#
# The O= assignment takes precedence over the KBUILD_OUTPUT environment
# variable.

Makefileを読んでしまえば今日の記事の内容はだいたいおしまいなのですが、一応説明を続けます。

make O=

まずはmake O=。カーネルビルドに必要なオブジェクトファイル群が、O=で指定したディレクトリに生成されるようになります。

$ make O=~/work/kernel-build/3.6.6-gentoo menuconfig
$ make -j5 O=~/work/kernel-build/3.6.6-gentoo
# make O=/home/tomo/work/kernel-build/3.6.6 modules_install install

ところで、Gentooでは各ユーザがそれぞれ自前でビルドしたKernelを利用するのが基本です。なので、
・パッケージマネージャは、必要に応じてユーザのカーネルが適切に設定されているかチェックできなければならない。
・ビルドにカーネルソースが必要なパッケージ(例えば外部カーネルモジュール)をインストールする場合、カーネルソース及びビルドオブジェクトがどこに存在するかパッケージマネージャは知っていなければならない。

ということで、上記make O= を利用してカーネルビルドすると、例えばこんなことになります。

# emerge --ask --verbose @module-rebuild
(snip)
 * NVIDIA-Linux-x86_64-304.64.run SHA256 SHA512 WHIRLPOOL size ;-) ...                       [ ok ]
 * Determining the location of the kernel source code
 * Found kernel source directory:
 *     /usr/src/linux
 * Found sources for kernel version:
 *     3.6.6-gentoo
 * Could not find a usable .config in the kernel source directory.
 * Please ensure that /usr/src/linux points to a configured set of Linux sources.
 * If you are using KBUILD_OUTPUT, please set the environment var so that
 * it points to the necessary object directory so that it might find .config.
 * ERROR: x11-drivers/nvidia-drivers-304.64 failed (setup phase):
 *   Kernel not configured; no .config found in /usr/src/linux
(snip)

カーネル設定をチェックするためにビルドシステムは.configファイルを探していますが、.configはO=で指定したディレクトリに作成されているので/usr/src/linux以下には見つからず、エラーになりました。

KBUILD_OUTPUT

ということで、今日の本題であるKBUILD_OUTPUT。この変数は環境変数として利用するのが普通です。Gentooでシステム環境変数を設定する場合は/etc/env.dを使います。

# echo 'KBUILD_OUTPUT="/home/tomo/work/kernel-build/current"' > /etc/env.d/90kbuild_output 
# env-update && source /etc/profile

$ cd ~/work/kernel-build
$ mkdir 3.6.6-gentoo && ln -sfn 3.6.6-gentoo current
(Kernelアップグレードの度にいちいち環境変数を切り替えるのは面倒なので、
 私はKernelバージョンごとのディレクトリ -> currentというシンボリックリンクにしています)

環境変数に設定したので、あとは普通にビルドするだけです。

$ cd /usr/src/linux
$ make menuconfig
$ make -j5
# make modules_install install
(sudoを利用する場合は、
 sudo実行環境に環境変数を渡すために/etc/sudoersにenv_keepを設定するか、
 sudo -lとして実行する必要があります)

ebuildビルドシステムは、KBUILD_OUTPUTを参照してカーネルビルドオブジェクトを探してくれます。

 * Determining the location of the kernel source code
 * Found kernel source directory:
 *     /usr/src/linux
 * Found kernel object directory:
 *     /home/tomo/work/kernel-build/current
 * Found sources for kernel version:
 *     3.6.6-gentoo
>>> Unpacking source...
...

ということで、GentooではKBUILD_OUTPUTを設定しておくと便利という話でした*2。一般ユーザでビルドする=rootでの作業を減らすという点よりも、ソースディレクトリとビルドディレクトリを分けておけば、パッケージマネージャでカーネルソースをアンインストールした時にきれいに消えてくれるし、git sourceを利用している場合もツリーを汚さずに済むのでよりエレガントではないでしょうか。

ちなみに、Linux Kernelのソースディレクトリ自体を別の場所に置きたい場合は、シンボリックリンク/usr/src/linuxをソースディレクトリにポイントする方法の他に、KERNEL_DIR変数を設定する方法もあります。これは基本的にパッケージマネージャだけが利用する変数なので、make.confにでも書いておけばいいでしょう。

# echo 'KERNEL_DIR=/home/tomo/work/src/linux/git-torvalds' >> /etc/portage/make.conf

Gentooのパッケージマネージャ = ebuildビルドシステムがどのようにカーネルオブジェクトディレクトリやカーネルソースディレクトリを検索するのかは、linux-info.eclassを見てください。例えば、ソースディレクトリにビルドオブジェクトが存在せず、KBUILD_OUTPUTも設定されていない場合には、/lib/modules/$(uname -r)/build symlinkがオブジェクトディレクトリであるとみなします。こうなってしまうと、「新しいKernelをビルド/インストールし、新しいKernelで起動する前にモジュール類を再ビルドしておく」状況で問題になりますので、注意する必要がありますね。

……

ところで、以前の記事でPaludisやpkgcoreを紹介した手前もあって、一般的にはPortageと書けば通じるところを、いちいち「Gentooのパッケージマネージャ」や「ebuildビルドシステム」などと書いているわけですが、迂遠なこと著しい……。

*1:私の「最近」は半年間くらいの期間があります

*2:たまーにKBUILD_OUTPUTを無視するパッケージもあります。Bug報告しましょう

GentooからPortageを完全に削除してPaludisを使おう

前回はPortageの代替としてPaludisとpkgcoreを紹介しました。では、いっそGentooからPortageを消してしまうとどうなるの、という話です。

Portageを消してしまおう

PaludisのFAQを見ると、こう書いてあります。

Remove Portage from my Gentoo installation

The short answer is that you can't.

Several Gentoo packages wrongly depend on Portage, several depend on Portage because they use it and there really is no reason to try. Just leave Portage installed.

この文書は既に古いという意見もあります。そこにはこう書いてあります。

Are there still lots of packages that use Portage but don't say so? e.g. gcc-config...

とにかく色々と問題はありそうなわけですね。

でも消します。

消してしまってから考える

cave uninstall sys-apps/portage --uninstalls-may-break virtual/package-manager --uninstalls-may-break app-admin/python-updater -x
rm -rf /etc/make.conf /etc/make.profile /etc/make.globals /etc/portage /usr/portage /var/lib/portage /var/cache/portage /var/tmp/portage

virtual/package-managerとapp-admin/python-updaterのportageへのDEPENDはいずれもchoice dependencyだったので、無視させました。
Portageの設定ファイルなどが残っていると、それを利用するコマンドもありそうなので、全て消します。また、gentooリポジトリやworldファイルの配置場所なども全てPortageのデフォルトと別の場所になるようにPaludisを設定しています*1

では問題をひとつひとつ解決していきます。

設定ファイルの更新コマンドがない

パッケージアップグレードでCONFIG_PROTECTされた設定ファイルが置き替えられる場合、それをマージするためのコマンドが必要です。etc-update, dispatch-confはsys-apps/portageに含まれるコマンドなので、Portageを消すと当然使えなくなります。以下の代替コマンドがあります。

このうち、Portage以外のパッケージマネージャ対応という点ではcfg-updateが一番良いようです。オプション--paludisを付けることでPaludisの設定を利用して実行されます。

cfg-update -l --paludis
cfg-update -u --paludis
env-updateがない

/etc/profile.envおよび/etc/ld.so.confの生成を行うenv-updateもsys-apps/portageに含まれているので、当然使えなくなります。これはapp-admin/eselectの基本モジュール、eselect.envで代替できます。

eselect env update
eselect env update --noldconfig

gcc-configが動かない

sys-devel/gcc-configはPortageが無いと動かないらしいですね。やってみましょう。

amd64vbox ~ # gcc-config -l
 * gcc-config: Could not get portage CHOST!
 * gcc-config: You should verify that CHOST is set in one of these places:
 * gcc-config:  - //etc/portage/make.conf
 * gcc-config:  - active environment

はい動きません。
システムにpythonがある場合にはportageqコマンドでCHOSTを取得してますね。あとで書きますが、Pythonは消せないので他の手段を探します。変数REAL_CHOSTが定義されていればそれを使ってくれるみたいなので、やってみます。

amd64vbox ~ # REAL_CHOST="x86_64-pc-linux-gnu" gcc-config -l
 [1] x86_64-pc-linux-gnu-4.4.6 *
 [2] x86_64-pc-linux-gnu-4.5.3
amd64vbox ~ # REAL_CHOST="x86_64-pc-linux-gnu" gcc-config 2
 * Switching native-compiler to x86_64-pc-linux-gnu-4.5.3 ...
/usr/bin/gcc-config: line 362: env-update: command not found

 * env-update failed to work properly; making sure ld.so.conf paths
 * are setup properly.  Please rerun gcc-config with the -f option.
 [ ok ]

今度はエラーにはなりませんでしたが、env-updateが無いと言われています。eselect env updateで代替できるのはわかっているので、ごまかします。

/usr/local/sbin/env-update
#!/bin/bash
eselect env update
binutils-configも動かない

sys-devel/binutils-configもgcc-configと似たようなことになります。

amd64vbox ~ # binutils-config -c
/usr/bin/binutils-config: line 314: portageq: command not found
 * binutils-config: No binutils profile is active!

変数CHOSTが正しく定義されていれば実行できます。

amd64vbox ~ # CHOST="x86_64-pc-linux-gnu" binutils-config -c
x86_64-pc-linux-gnu-2.21.1

実際に設定を変更する場合には、gcc-configと同様にenv-updateが呼ばれます。これまたgcc-config同様、env-update実行が失敗しても警告を出すだけで正常終了します。

ちなみに、gcc-config, binutils-configはgccbinutilsのインストール時にtoolchain{-binutils}.eclassから呼ばれます。Paludisのebuild処理においてはCHOST及びREAL_CHOSTが定義済みなので、エラーにはなりません*2

どこかで問題が出るかもしれませんが、REAL_CHOSTとCHOSTを環境変数にしてしまえば毎回指定しなくても済みますね。

/etc/env.d/00real-chost
REAL_CHOST="x86_64-pc-linux-gnu"
CHOST="${REAL_CHOST}"

Gentoo黒魔術の大物、Java

Java周りはかなりやっかいです。

dev-java/java-config-wrapperに含まれるjava-1.5-fixerはemergeを使用します。java-check-environmentはportageqを使用します。なので、Portageを削除すると色々と動かなくなります。
dev-java/java-configに含まれるdepend-java-queryはPortagePython Moduleを利用するので、正常に動かなくなります。

それだけなら別にいいんですが(よくない)、この辺りのコマンド類はjava-*.eclassで利用されるため、Java関係のパッケージのインストールが軒並み失敗するようになります。
例えばこんな感じ。

>>> Starting pkg_setup
!!! ERROR: No module named portage_dep
 * Unable to determine VM for building from dependencies:

Error:
  * In program cave perform install --hooks --managed-output --output-exclusivity with-others =media-libs/libjpeg-turbo-1.2.0-r1:0::gentoo --destination installed --replacing =media-libs/libjpeg-turbo-1.2.0-r1:0::installed --x-of-y 15 of 15:
  * When installing 'media-libs/libjpeg-turbo-1.2.0-r1:0::gentoo' replacing { 'media-libs/libjpeg-turbo-1.2.0-r1:0::installed' }:
  * When running an ebuild command on 'media-libs/libjpeg-turbo-1.2.0-r1:0::gentoo':
  * Install failed for 'media-libs/libjpeg-turbo-1.2.0-r1:0::gentoo' (paludis::ActionFailedError)

NV_DEPEND: !media-libs/jpeg:0
	amd64? ( || ( dev-lang/nasm dev-lang/yasm ) )
	x86? ( || ( dev-lang/nasm dev-lang/yasm ) )
	amd64-linux? ( || ( dev-lang/nasm dev-lang/yasm ) )
	x86-linux? ( || ( dev-lang/nasm dev-lang/yasm ) )
	java? ( >=virtual/jdk-1.5 )           java? ( >=dev-java/java-config-2.1.9-r1  )    

!!! ERROR in media-libs/libjpeg-turbo-1.2.0-r1::gentoo:
!!! In java-pkg_switch-vm at line 4473
!!! Failed to determine VM for building.

!!! Call stack:
!!!    * java-pkg_switch-vm (/var/tmp/paludis/media-libs-libjpeg-turbo-1.2.0-r1/temp/loadsaveenv:4473)
!!!    * java-pkg_init (/var/tmp/paludis/media-libs-libjpeg-turbo-1.2.0-r1/temp/loadsaveenv:3950)
!!!    * java-pkg-opt-2_pkg_setup (/var/tmp/paludis/media-libs-libjpeg-turbo-1.2.0-r1/temp/loadsaveenv:3211)
!!!    * pkg_setup (/var/tmp/paludis/media-libs-libjpeg-turbo-1.2.0-r1/temp/loadsaveenv:5188)
!!!    * ebuild_f_setup (/usr/libexec/paludis/0/pkg_setup.bash:43)
!!!    * ebuild_main (/usr/libexec/paludis/ebuild.bash:646)
!!!    * main (/usr/libexec/paludis/ebuild.bash:672)

diefunc: making ebuild PID 11027 exit with error
die trap: exiting with error.

Failed install to / for media-libs/libjpeg-turbo-1.2.0-r1:0::gentoo replacing 1.2.0-r1:0::installed

このエラーに関しては、depend-java-queryはPortageのモジュールを利用しなくても実はちゃんと動いている(ように見える)ので、豪快にごまかしてみました。

--- src/java_config_2/VersionManager.py.orig
+++ src/java_config_2/VersionManager.py
@@ -132,7 +132,7 @@
 
             # Remove conditional depends that are not turned on
             atoms = " ".join(flatten(use_reduce(paren_reduce(atoms),uselist=use)))
-        except KeyError:
+        except (ImportError, KeyError):
             pass
         return atoms

上記のエラーはmedia-libs/libjpeg-turbo[java]で発生したものですが、取り敢えずこれでインストールはできるようになりました。

取り敢えず他にも試そうということで、dev-java/icedtea6-binとdev-java/sun-jdkの両方を入れて切り替えつつapp-editors/jeditをインストールしてみたりしましたが、今のところ問題は出ていません。
私が普段使っている環境ではJavaを利用するパッケージが少ないので、問題に遭遇することも少なかったのですが、ガシガシJava関連パッケージを利用する場合にはどこかでドツボに嵌りそうな予感がひしひしとします。

その他、特に問題のないところ

  • app-admin/perl-updater, app-admin/*-updater

perl-cleaner, *-updaterなどのコマンドは、対象のアップグレードが行われたあとに実行すると、更新が必要なパッケージを自動的に再インストールしてくれます。perl-cleanerは>=2.8からPaludis(caveコマンド)に対応しています。haskell-updater, python-updaterは現在gentooリポジトリにあるものは全てPaludisに対応しているようです。

  • sys-auth/pambase

pambaseのebuild内でqatomコマンドを使っているのでapp-portage/portage-utilsにDEPENDしています。幸い、qatomの動作はパッケージマネージャに依存しないので、Portaeを削除していてもちゃんと動作します。pambaseのインストールも問題ありません。

  • revdep-rebuild

前回の記事にも書きましたが、Paludisにはrevdep-rebuildに対応する機能が最初からあります。

cave fix-linkage

感想と結論

Portageを消すことで、「Gentooとは選択です」を実現するための黒魔術の一端に触れることができました。えっそんな結論。

Portageを消すことで色々と問題には遭遇しますが、なんとかごまかしつつ、使えています。私が普段使っている程度の環境を運用するのなら特に問題ない、と言えなくもないというところです。しかしこれまでは何とかなっていますが、次に遭遇するトラブルは解決できるとは限らないし……。

そして、実はPortageを消しても特にメリットはないので、まあやらなくても良いですかね。現状では基本的に自己満足の世界です。そして私的には結構満足なのです*3

そうだ、Portage消せるのならPythonも要らないんじゃね!?

と思ったのですが、sys-apps/paludisはdev-cpp/gtestにDEPENDしており、dev-cpp/gtestはdev-lang/pythonにDEPENDしていました……。つまりPortageを消してもPythonは必要でした。うーん残念。

cave importのご紹介

gcc-configが警告を出さなくなるように、/usr/local/sbin/env-updateというスクリプトを手動で用意しましたが、/以下のファイルは須らくパッケージマネージャの管理下にあるべし、という方にはcave importがオススメです。

まずinstalled_unpackagedリポジトリを用意しておきます。

amd64vbox ~ # cat /etc/paludis/repositories/installed_unpackaged.conf 
format = installed_unpackaged
location = /var/db/paludis/repositories/installed_unpackaged
amd64vbox ~ # mkdir -p /var/db/paludis/repositories/installed_unpackaged
amd64vbox ~ # chown paludisbuild:paludisbuild /var/db/paludis/repositories/installed_unpackaged

適当にディレクトリを掘って、インストールしたいファイルを配置します。

amd64vbox ~ # mkdir -p DESTDIR/usr/sbin
amd64vbox ~ # cp env-update DESTDIR/usr/sbin
amd64vbox ~ # mkdir -p DESTDIR/etc/env.d
amd64vbox ~ # cp 00real_chost DESTDIR/etc/env.d

適当なカテゴリ/パッケージ名を指定してcave importします*4

amd64vbox ~ # cave import --location ./DESTDIR sys-apps/portage-compat  -x

すると、こんな具合にPaludisで管理できます。

amd64vbox ~ # cave show sys-apps/portage-compat
* sys-apps/portage-compat
    ::installed-unpackaged    0 {:0}
    sys-apps/portage-compat-0:0::installed-unpackaged (world)
    Description               
    Installed time            Mon Mar 19 19:15:31 JST 2012
    Source repository         unpackaged

amd64vbox ~ # cave contents sys-apps/portage-compat
/usr
/usr/sbin
/usr/sbin/env-update
/etc
/etc/env.d
/etc/env.d/00real_chost

消したくなったらcave uninstallで消せます。cave importは依存関係も記述できるのですが、blockerには対応していない*5のがちょっぴり残念です。

*1:VDBはPMSの仕様なので/var/db/pkgのままです

*2:env-updateが無いというwarningは当然は出ます

*3:所詮自己満足の世界なので、現時点では関連するバグレポも投げていません

*4:注意! インストール対象のファイルはmvされますので、元のファイルは消えます

*5:DEPEND=!sys-apps/portageとかは実現できない

Portageの代わりにPaludisを使ってみよう。

PortageGentooシステムの中核をなすパッケージマネージャであり、Gentooの最大の魅力とも言われています。
つまりGentoo = Portage? いいえ!!

Gentooとは選択です❞

Gentooは選択なので、パッケージマネージャもまた選択の対象です。sys-apps/portageGentooの中核パッケージセット = system setには含まれていません。ではsystem setで要求されているのは何かと言うと、virtual/package-managerです。virtual/package-managerでは以下の3パッケージが列挙されており、いずれかがインストールされます。

残念ながら、portage以外は全てunstable扱いです。更に残念なことに、Portageに暗黙に依存しているパッケージ*1も存在するために、PortageGentooからはぎ取るのは結構困難ですね*2……。
でも普段はパッケージマネージャとしてPortage以外を使ったって全然問題ないのです。

Paludisを使おう。

そんなわけでPaludisを使いましょう。Paludisには以下のような特徴があります。

  • C++で書かれている。だからと言ってすごく速かったりはしませんが。
  • 複数のパッケージリポジトリを利用できる。Portageで言うところのOverlayの概念は必要ないということですね。
  • ebuildヒューリスティクスが色々と違う。
  • ebuild以外のビルドシステムにも対応している
  • 豊富なHookを利用できる。
  • 依存関係グラフを書いたり、既存のディレクトリをパッケージマネジメントに組み込んだりと言った面白楽しい機能。
  • その他いろいろ。

インストールするには。

emergeしましょう。それだけだ。eselect package-manager set paludisもしておくと良いかも。

Paludisの設定。

Paludis公式サイトのGetting Startedを読みましょう。
portage USEフラグを有効にすると一応Portage用の設定ファイルを読み込んで動作しますが、あまり当てにならないので自分でPaludis専用の設定ファイルを書くのが確実です。portage2paludisスクリプトを利用して自動作成し、説明を読みながら書き直すのが一番簡単かなと思います。

Paludisの利用。

Portageにおけるemergeに対応するのはcaveコマンドです。
(ついでに言えばpkgcoreではpmerge, pmaintです)
以下の例ではportage-2.2_alpha86、paludis-0.72.0、(ついでにpkgcore-0.7.7.8)を使用しています。

emerge --sync
cave sync
pmaint sync

caveのsyntaxルールはcave アクション 引数 (-オプション)という具合で、gitっぽい感じですね。
Paludisとpkgcoreはマルチリポジトリ対応なので、syncすると登録された全てのリポジトリが同期されます。

  • パッケージのインストール
emerge パッケージ名
cave resolve パッケージ名
pmerge パッケージ名

caveは対話的に動作しませんので、emergeの-a, --askや-p, --pretendに対応するオプションはありません。
オプション無しでcave resolveを実行するとemerge --pretend同様に実際には何もしません。
本当にアクションを実行する場合にはオプション-x, --executeを付けます(幾つかのアクションは-xオプションがなく、即時実行されます)
caveには -v, --verboseに対応するオプションは無く、常に饒舌です。

インストールにあたってUSE flagsやACCEPT_KEYWORDに修正が必要な場合にサジェストしてくれるのはいずれのパッケージマネージャも同じですが、総じてPortageのメッセージの方が親切でわかりやすいように思います。
Blockerがあった場合も解決方法をサジェストしてくれますが、Paludisでは-U, --permit-uninstallや-d, --permit-downgradeなどのオプションを使うとUpgrade時のBlockerを自動的に処理できたりします。Blocksパッケージを手動でアンインストールしなくて良いということですね。

  • パッケージのアンインストール
emerge -c パッケージ名
(emerge --depclean パッケージ名)
cave uninstall パッケージ名
cave resolve \!パッケージ名
pmerge --unmerge パッケージ名

resolveの引数に!パッケージ名とすると、インストールの逆の動作、つまりアンインストールします(シェルが"!"を解釈しないようにエスケープする必要があります)。
pmergeには依存関係を考慮して削除するオプションはありません。

  • インストール済みのシステムを最新に更新する。
emerge -uDN --with-bdeps=y world
(emerge --update --deep --newuse --with-bdeps=y world)
cave resolve world -B -ks -Sx -sx
pmerge --upgrade --deep --newuse --with-built-depends @system @world

emergeでの例は--with-bdeps=yなので、これはややパラノイア的ですね。またemergeの-N, --newuseオプションはebuild内部でのUSE Flag変更も検知します。Paludisとpkgcoreはそこまでしません。

caveの面白いオプションとしては、-R, --reinstall-scmというのがあります。SCMパッケージ(いわゆるlive ebuild)が最後にインストールされたのが一日以上前(-Rd)あるいは一週間以上前(-Rw)であれば、再インストールします。
caveにオプション-c, --completeを付けると-ks -Rw -Sa -sa -Bを付けたのと同じ意味になります。

PortageやPaludisと違い、pkgcoreでのworld setはsystem setを暗黙に含まないので、ターゲットに両方を列挙しています。

  • システムから不要なパッケージを削除する。
emerge -c
(emerge --depclean)
cave purge
pmerge --clean --with-built-depends

後で詳しく書きますが、PaludisではSLOT及びchoice dependencyの扱いがPortageやpkgcoreと違っています。なのでemerge --depcleanで消えるパッケージがcave purgeでは消えないことがしばしばあります。

  • モー!!
emerge --moo
cave moo

pkgcoreではLarry君に逢えませんね……。バグでしょうか。

マルチリポジトリ、さらに色々なリポジトリ

Paludisは複数リポジトリに対応しています。

Gentooユーザにとっては基本となるのはgentooリポジトリですね。おっと、Paludisの文書でも釘を刺されていますが*3gentooリポジトリPortageツリーなんて言ってはいけません。Portage以外でも使えるわけですから。gentooリポジトリgentoo-x86などと呼びましょう;-)

gentooリポジトリの設定例。

/etc/paludis/repositories/gentoo.conf

location = /usr/portage
sync = rsync://rsync.jp.gentoo.org/gentoo-portage
profiles = /usr/portage/profiles/default/linux/x86/10.0
distdir = /usr/portage/distfiles
format = e
names_cache = /var/cache/paludis/names

Gentooで利用できるebuildリポジトリはたくさんあります。それらもgentooリポジトリと同様に書くことができます。自分で書くのが面倒くさい場合には、app-portage/laymanから利用できるリポジトリに関しては、playmanコマンド(Paludisのruby-bindings USEフラグを有効にするとインストールされます)をlaymanの代わりに使えます。ただし、(p)laymanで提供されるリポジトリに関しては次に書くunavailable/repositoryリポジトリを使って利用する方法が推奨されています。

repositoryリポジトリとunavailableリポジトリ

unavailableリポジトリは「まだ設定されていないリポジトリ」の情報を保持するリポジトリです。
repositoryリポジトリはunavailableリポジトリの情報を元に「リポジトリ設定を自動構成するためのリポジトリ」です。
リポジトリ」がゲシュタルト崩壊しつつあります。

unavailableリポジトリの設定。

/etc/paludis/repositories/layman.conf

format = unavailable
name = layman
location = /var/db/paludis/repositories/layman
sync = tar+http://git.exherbo.org/layman_repositories.tar.bz2
importance = -100

Overlayの情報をダウンロードして利用するlaymanという名のunavailableリポジトリを作りました。

repositoryリポジトリの設定。

/etc/paludis/repositories/unavailable.conf

format = repository
config_filename = /etc/paludis/repositories/%{repository_template_name}.conf
config_template = /etc/paludis/repository.template

リポジトリ設定ファイルのテンプレートとして/etc/paludis/repository.templateも作ります。

format = %{repository_template_format}
location = /var/db/paludis/repositories/%{repository_template_name}
sync = %{repository_template_sync}
master_repository = gentoo

すると、普通のパッケージのようにインストールすることでリポジトリを追加できます。

cave resolve repository/betagarden -x -1

便利な副作用として、unavailableリポジトリは各リポジトリに含まれるパッケージ情報を含むので、まだインストールされていないリポジトリに含まれるパッケージもcaveコマンドで参照できるようになります。eix-remoteみたいな感じですね。

installed-unpackagedリポジトリ

cave importコマンドで、パッケージングがされていないディレクトリツリーをあたかもパッケージであるかのようにインストールできます。installed-unpackagedリポジトリはそのためのリポジトリです。
例えば、ごく一般的にソフトウェアをビルドして、

$ configure --prefix=/usr && make DESTDIR=/home/user/path install

出来上がったソフトウェアのディレクトリツリーをcave importします。

# cave import --location /home/user/path hoge/fuga

すると、/home/user/path以下のディレクトリ構成がhoge/fugaというパッケージとして/にインストールされ、Paludisで管理できるようになります。必要であればオプションで依存関係も定義できます。
空のディレクトリをcave importすれば、Portageのpackage.providedの代わりになりますね。

ebuildは簡単に書けるのが売りですが、ソフトウェアのビルド形態・配布形態によっては面倒なこともあります。そういう時にはかなり便利ではないでしょうか。ちなみに、Paludisはインストール時にファイルの衝突を検知しないので、システムを壊すようなディレクトリツリーをインストールしてしまわないように気を付けましょう……。

Paludisの提供する機能。

Paludisは、Portageでは外部ツールで提供されている機能を内蔵しています。

  • パッケージの詳細情報を表示します。
cave show パッケージ名
eix パッケージ名 (app-portage/eix)

showアクションのターゲットはパッケージ名だけでなく、setやrepositoryにもできます。

cave show repository/betagarden (betagardenリポジトリの情報を表示)
cave show @system (system set に含まれるパッケージを列挙)
  • パッケージに含まれるファイルを列挙します。
cave contents パッケージ名
qlist パッケージ名 (app-portage/portage-utils)
equery files パッケージ名 (app-portage/gentoolkit)
  • ファイルがどのパッケージに含まれているのか示します。
cave owner ファイル
qfile ファイル (app-portage/portage-utils)
equery belongs ファイル (app-portage/gentoolkit)
  • ライブラリのsoname変更で壊れたパッケージをインストールしなおす。
cave fix-linkage
revdep-rebuild (app-portage/gentoolkit)

ちなみに、pkgcoreではpqueryというコマンドでパッケージその他の情報を表示でき、これも非常に強力です。

外部ツールとの連携とか。

VDB*4Portageと互換性があるので、app-portage/eixやapp-portage/gentoolkit, app-portage/portage-utilsなどに含まれるユーティリティも一部は利用できます。ただし多くのコマンドがPortageの設定ファイルやログファイルを元に情報を表示するので、正しい結果が得られないことがあります。

非互換部分。

Paludisは自身の扱うファイル/ディレクトリのownerがpaludisbuild:paludisbuildであった方が色々と幸せですが、Portageやpkgcoreと混ぜて使うとパーミッションが変わってしまいます。まあ混ぜなきゃ良いんだよ。

  • worldファイルの扱い。

Portageでは、ユーザがsetをインストールするとworld_setsファイルに追加されます。特殊なset(@preserved-rebuildなど)はemergeしてもworld_setsには追加されません。
Paludisでは、cave resolve setしてsetをインストールするとworldファイルにsetを追加します。また、unavailableリポジトリを利用してリポジトリをインストールすると、それもworldに追加されます。いずれの場合も、Portageとの互換性は破壊されます。

worldファイルに記述されるパッケージ情報には、SLOTを含めることができます。

emerge --noreplace sys-kernel/gentoo-sources:3.2.1-r1

cave update-worldコマンドの引数となるターゲットはSLOT情報を含んではいけないので、以下はエラーになります。

cave update-world sys-kernel/gentoo-sources:3.2.1-r1

しかしPaludis自体はworldに記述されたSLOT情報を正しく認識します。ですから必要ならば直接worldを編集すれば良いですね。
pkgcoreはworldに書かれたSLOT情報を無視します。

Paludisでは、複数SLOTが存在するパッケージに対して依存元がSLOTを明示していない場合、全てのSLOTが必要であると解釈されます。例えばバージョン違いの複数のsys-kernel/gentoo-sourcesをインストールしている場合、各バージョンに応じたSLOTで分けられていますが、virtual/linux-sourcesがどのSLOTを必要としているかは不明なので、全てのSLOTが保持されます = cave purgeで削除されません。同様に、worldに記録されているパッケージも、SLOTが明記されていなければ、全てのSLOTが保持されます。

Portageの場合は依存SLOTが明示されていない場合は最新のSLOTのみを残します。

choice dependencyとはebuild

RDEPEND="|| (
	    category/pkg1
	    category/pkg2
	    ....

のように書かれている依存関係で、列挙されているうちのいずれかがインストールされれば依存関係を満たします。Portageの場合、もっともリストの先頭に近いパッケージのみが保持され、他は(別の依存関係に組み込まれていなければ)--depcleanで消えます。しかしPaludisでは列挙されているいずれのパッケージも削除されません。こちらはちょっと理屈と合わない気もしますけど、よくわかりませんでした。誰か説明してくれるとうれしいです。

  • VDBの非互換。

VDBは概ね互換性がありますが、保存されるenvirionment.gzには相違点があります。Paludisとpkgcoreは非互換で、Paludisでインストールしたパッケージをpkgcoreでアンインストールしたりするとエラーになります(その逆は大丈夫です)。また、Portage <-> pkgcore, Portage <-> Paludisの相互運用は問題ないようです。

まとめ。

Paludisを使ってみると、PortageGentooの特徴/魅力のごく一部分だということがわかります。Gentooの特徴といえば第一にパッケージマネジメントについて挙げる方が多いと思いますが、その魅力のほとんどはPMS*5ebuild*6、そしてgentooリポジトリの特徴であり、Portageに限定されたものではないのです。Portageを使わなくてもGentooGentooだよ!!

それともうひとつ、今気付いたのですが、Paludisを入れておくと万が一Portageが壊れてしまった時にとても役に立ちますね。これでもうPythonを壊してしまっても大丈夫というわけです。でもVDBが壊れたらダメなものはダメなんだけど*7

ということでみんなもPaludisを使ってみませんか。
Paludisだけ使っているとGentoo本家へのBug Report時にちょっと困っちゃうんですけどね……。

*1:sys-devel/gcc-configとか

*2:実際にPortageを削除してしまったらどうなるかもいずれ試します

*3:The main Gentoo repository, sometimes annoyingly called 'the Portage tree'

*4:/var/db/pkg: インストール済みパッケージのデータベース

*5:Package Management Specification app-doc/pms

*6:man 5 ebuild

*7:If you deleted /var/db/pkg by accident, you're pretty much screwed. So don't do that, and be more careful next time. If you deleted /var/db/pkg on purpose, please install OS X and stop pestering us.

Gentooとは選択です。

"Gentooとは選択です" Gentooハンドブック

"人生とは選択の総和です" アルベール・カミュ*1

なるほど。
Gentooとは人生です。

Fateは文学CLANNADGentoo

*1:ググって見つけた言葉だから出典は示せないんだ。すまない

Anaconda Gentoo Installerを使ってみたよ。

起動前の注意点。

anacondaは本来、テキストコンソール上でもX上でも動くようにデザインされていますが、Anaconda Gentoo InstallerはX11 GUIしか用意されていませんので、X上の端末エミュレータでliveinstと打って起動しましょう。一般ユーザで起動して大丈夫です。
また、Oxygen-Gtkテーマエンジンのバグ(たぶん)により、当該テーマを使用していると途中でUIが応答しなくなります。Gentoo LiveDVD 11.2ではKDE及びXfce4のデフォルトテーマがOxygen-Gtkなので、Gtkテーマを変更するか、他のデスクトップ環境/ウィンドウマネージャを利用しましょう。

実行中は特に面白いこともないので箇条書きしますよ。

  • 言語設定では、選択する言語に対応するlocale dataを先に作っておきましょう。

  • この画面でCFLAGSなどが設定できます。ここで-march= を未定義のまま進めると、後でgccが呼ばれたときにerror: bad value (None) for -march= switch という素敵なエラーを食らいます。ちゃんと設定しましょう。
  • ミラーサイト設定画面があるのですが、これはモックアップで実際には何も設定されません……。
  • System ProfileとUSE Flagsを選択できます。ただし設定できるのはGlobal USE Flagsのみで、各パッケージ個別のUSE Flagsは設定できません。またVIDEO_CARDSやINPUT_DEVICEなども選択できないので、巨大なパッケージをインストーラから入れるのはちょっと困難な気がします。
  • カーネルはLiveDVDからコピーして利用しますので、設定は事実上存在しません。

  • インストールパッケージを選択できます。上記の通り、細かなUSE Flagsの設定ができませんので、たくさんのパッケージが入るGnomeとかはちょっと大変っぽいですね(後からここでFluxboxを選択して試したところ、ちゃんとemergeが完走して起動できました)。
  • ネットワーク設定がありません。有線ネットワークでDHCPにぶら下がっているような構成よりもちょっと複雑な環境で使いたい場合には、インストーラが完走した後に再度chrootして環境を整える必要があるでしょう。

インストール完了!!


そんな感じでインストール完了後のGentoo Linuxの様子です。make.confはほぼ空っぽ、syslogとcronは入っていますが起動していません。ネットワークはbusyboxDHCPクライアント機能で動作しています。

起動してネットワークに繋がっていればなんとでもできるのがGentooですので、後はお好み次第です!!

おまけ。tinderboxからバイナリパッケージを取得して環境を作ってみた。

#gentooinstallbattleのレギュレーションに従うと、net-misc/mikutterでつぶやかないとGentooのインストールは完了しません。パフォーマンスに劣るVirtualBox上で手早くemergeを進めるために、バイナリパッケージ主体で環境を作ってみました。

# /etc/make.confに次のように書きます。
# amd64の場合。x86の場合はURIの最後がx86になります
PORTAGE_BINHOST="http://tinderbox.dev.gentoo.org/default-linux/amd64"

あとは、emerge --getbinpkgonlyで必要なパッケージを揃えて行きます。バイナリパッケージをemergeすると当然のことながらUSE Flagsは調整できませんし、ときどきパッケージごとのバージョンミスマッチがあったりしていやんな感じです。具体的にはxorg-serverとx11-drivers/*にバージョンミスマッチがあってドライバを作り直す必要がありましたし、いくつかのパッケージはpython:2.6に依存していたのでそれも普通にソースからemergeする必要がありました。


結果どれくらい素早くインストールが完了したかというと……たぶん、試行錯誤した時間も考慮に入れると、普通にコンパイルしながら最小構成でmikutterを動かせるようになるまでの時間とたいして変わらなかった気もしますね。