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報告しましょう