Linuxカーネル、busybox、buildrootではKbuildというコンフィグ設定のフレームワークが使用されています。
今回はbusyboxのkbuildの使い方について記載します。
1. Kbuildのメリット
Linuxカーネル、busybox、buildrootは大量に設定項目が存在します。その設定項目に沿って、どのファイルをビルドするか、マクロの値はどうなるかが変わっていきます。その結果、生成されるバイナリが変わってきます。
Kbuildを使わなかった場合はMakefileが複雑になりがちです。 例えば以下のファイルがあるとします。
$ ls bye.c hello.c Makefile print.c print.h
print.cの内容は以下の通りです。 print関数を定義しており、DEBUGというマクロが定義されているかいないかでプレフィックスに<debug>をつけて文字列を出力する場合とつけないで文字列 を出力する場合に切り替えます。
#include <stdio.h> #ifdef DEBUG void print(const char *text) { printf("<debug> %s\n", text); } #else void print(const char *text) { puts(text); } #endif
print.hの内容は以下の通りです。print.cで定義した関数を参照できるようにします。
#ifndef __PRINT_H #define __PRINT_H void print(const char *text); #endif /** __PRINT_H */
hello.cの内容は以下の通りです。print関数にhelloという文字列を渡します。
#include "print.h" int main(void) { print("hello"); return 0; }
bye.cの内容は以下の通りです。print関数にbyeという文字列を渡します。
#include "print.h" int main(void) { print("bye"); return 0; }
Makefileの内容は以下の通りです。hello.cとprint.cを用いたプログラムとbye.cとprint.cを用いたプログラムを生成する為にhelloというターゲットとbyeというターゲットを作成します。
hello: $(CC) $(CFLAGS) hello.c print.c -o hello bye: $(CC) $(CFLAGS) bye.c print.c -o bye clean: rm -rf hello bye
DEBUGを無効にする場合には以下のようにします。
$ make hello $ ./hello hello $ make bye $ ./bye bye
DEBUGというマクロを有効にする場合は以下のようにします。
$ make hello CFLAGS=-DDEBUG $ ./hello <debug> hello $ make bye CFLAGS=-DDEBUG $ ./bye <debug> bye
hello.cとbye.cの切り替え、冗長なprint.cの記述、CFLAGSのDEBUGの設定と、単純な場合でも複雑になりがちです。大量の設定項目がある場合はMakefileが膨大になります。
Kbuildはこれらのファイルの取捨選択とマクロの設定方法を提供するフレーム ワークです。Kbuildを用いることでMakefileがコンパクトになり、ビルドする際にどの設定が有効になるかが理解がしやすくなります。
2. 使い方
Kbuildはmakeコマンドにターゲット名を指定することで使用します。busyboxで利用可能な主なターゲットは以下の通りです。
Target | Abstract |
---|---|
make config | CUIベースの設定。make menuconfigの方が良い。 |
make xxx_defconfig | デフォルトコンフィグ。後述。 |
make menuconfig | Ncursesベースの設定。後述。 |
make oldconfig | CUIベースの設定。後述。 |
その他のターゲットはmake helpで確認できます。
2.1. .configとinclude/autoconf.h
.configはmake %configでの結果を保存するファイルです。さらにinclude/autoconf.hに.configの内容が転写されます。 include/autoconf.hは$(CC) -include include/autoconf.hのようにコンパイルコマンドと一緒にインクルード指定されます。
2.3. make xxx_defconfig
configsディレクトリ配下のxxx_defconfigを.configに反映します。例えば、 configs/freebsd_defconfigの場合、make freebsd_defconfigで反映できます。 make freebsd_defconfigとcp configs/freebsd_defconfig .configはほぼ等価です。'ほぼ'というのは、xxx_defconfigにて指定されるべき設定項目が指定されていない場合にmake xxx_defconfigを実行した場合は自動的に設定される のに対し、cp configs/freebsd_defconfigの場合は自動的に設定されません。
ソースツリーが更新された際に、xxx_defconfigの内容が、実際の設定項目とマッチしないことで、指定されるべき設定が指定されていないケースが発生します。
busybox-1.22.1では以下のデフォルトコンフィグが存在します。
android_defconfig android2_defconfig android_ndk_defconfig freebsd_defconfig cygwin_defconfig TEST_nommu_defconfig TEST_noprintf_defconfig TEST_rh9_defconfig
2.4. make menuconfig
Ncursesベースのインターフェースを提供する設定です。.configが既にある場合はそれに乗っ取って設定項目を表示します。NcursesなのでSSH経由でも利用可能です。以下はSSHで接続したLinux上でmake menuconfigを実行した画面です。
2.4.1. 操作方法
make menuconfigでは以下のキーを使います。
Key | Abstract |
---|---|
ARROW UP | 設定項目のカーソルを上へ移動 |
ARROW DOWN | 設定項目のカーソルを下へ移動 |
ARROW RIGHT | <Select> <Exit> <Help>のカーソルを右へ移動 |
ARROW LEFT | <Select> <Exit> <Help>のカーソルを左へ移動 |
SPACE | コンフィグのON/OFF |
ENTER | 設定項目グループへ移動 |
ESC ESC | 元の設定項目グループへ移動、またはセーブへ。<Exit>と等価。 |
h | 設定項目の詳細を表示 |
/ | 設定項目のキーワード検索 |
'h'キーの詳細画面は以下の通りです。
セーブ画面は以下の通りです。トップの設定画面でESCを2回押す、あるいは<Exit>を選択すると表示されます。
2.5. make oldconfig
アップデートされたソースツリー(あるいは逆に版数が古いもの)に対して、 以前のソースツリー用の.configを用いる場合、設定項目の違いによる不整合が起こる場合があります。
make oldconfigは設定項目で不整合があるものを一つずつ確認し、設定し直すことが可能なターゲットです。
また、不整合がある.configでmake menuconfigを実行した場合、自動的に設定が反映されます。
3. コンフィグのカスタマイズ方法
実際にdefconfigにエラーがあった場合の修正手順を示します。 busybox-1.22.1のandroid_ndk_defconfigを用いた場合、以下のエラーがあります。
CC coreutils/touch.o coreutils/touch.c: In function 'touch_main': coreutils/touch.c:171:21: error: 'lutimes' undeclared (first use in this function) (opts & OPT_h) ? lutimes : ^
ソースコードの該当箇所は以下です。ENABLE_FEATURE_TOUCH_NODEREFという設定が有効な場合に実行されるコードのようです。
#if ENABLE_FEATURE_TOUCH_NODEREF (opts & OPT_h) ? lutimes : #endif
make menuconfigの'/'キーでFEATURE_TOUCH_NODEREFを検索してみます(ENABLE_というプレフィックスはFEATURE_TOUCH_NODEREFが有効な場合につくプレフィックスです)。
以下の設定を無効にしました。
結果、coreutils/touch.oはビルドできるようになりました。
CC coreutils/touch.o CC coreutils/tr.o
4. まとめ
busyboxのkbuildについて簡単にまとめてみました。Linuxカーネルや buildrootについても若干の違いはあるものの、同様の手順でカスタマイズで きるかと思います。