12 February 2012

Linux Kernel の Makefile がどういう仕組みになっているのか前から知りたいと思っていました。

O’REILLY の 「GNU Make」 という本の11章に Linux Kernel の makefile について少し解説があります。 もちろん makefile の仕組み全体は説明してないのですが、いくつかの興味深いトピックについて解説されていて、一読の価値があります。

まずはトップの Makefileの大枠から見てみます。

これは O’REILLY 本で解説されています。

トップのMakefileの100行目付近は以下のようになっています。(途中、あんまり関係ない部分は省略しています。)

ifeq ($(KBUILD_SRC),)

# OK, Make called in directory where kernel src resides
# Do we want to locate output files in a separate directory?
ifeq ("$(origin O)", "command line")
  KBUILD_OUTPUT := $(O)
endif

  <snip>

# That's our default target when none is given on the command line
PHONY := _all
_all:

  <snip>

ifneq ($(KBUILD_OUTPUT),)

  <snip>

PHONY += $(MAKECMDGOALS) sub-make

$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make
        @:

sub-make: FORCE
        $(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \
        KBUILD_SRC=$(CURDIR) \
        KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile \
        $(filter-out _all sub-make,$(MAKECMDGOALS))

# Leave processing to above invocation of make
skip-makefile := 1
endif # ifneq ($(KBUILD_OUTPUT),)
endif # ifeq ($(KBUILD_SRC),)

# We process the rest of the Makefile if this is the final invocation of make
ifeq ($(skip-makefile),)

ぱっと見面食らうコードなので説明します。この部分は、 KBUILD_OUTPUT (O変数)を処理しています。

$ make O=<directory>

のようにすると、オブジェクトファイルなどの生成ファイルを <directory> ディレクトリに出力するので、ソースツリーを汚さなくて済みます。 (make で指定できる変数については Documentation/kbuild/kbuild.txt に説明があります。)

ifeq ("$(origin O)", "command line")
  KBUILD_OUTPUT := $(O)
endif

というコードにもあるように、出力ディレクトリは、環境変数 KBUILD_OUTPUT で与えることも コマンドラインで O= で与えることもできますが、 O= のほうが優先されます。

例えば、Linux のソースのトップで

$ make O=/home/hoge/out piyo fuga

と打ち込んだとします。 KBUILD_SRC は空で、 KBUILD_OUTPUT は空ではないので、上記コードを通ります。 make は piyofuga を構築するルールを見つけようとします。

$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make
        @:

にルールが見つかります。(MAKECMDGOALSpiyofuga が入っているので)

piyofugasub-make に依存することがわかります。 @: の部分は何もしません。 sub-makeの構築ルールは以下です。

sub-make: FORCE
        $(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \
        KBUILD_SRC=$(CURDIR) \
        KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile \
        $(filter-out _all sub-make,$(MAKECMDGOALS))

これは、 -C $(KBUILD_OUTPUT) でわかるように、出力ディレクトリへ移動して、再帰的に make piyo fuga を実行しています。 なお、 skip-makefile1 がセットされているので、 ifeq ($(skip-makefile),) 以下は見えていません。 トップの Makefile は1500行ほどありますが、残りの大半は ifeq ($(skip-makefile),) の中にいます。

再帰的に呼び出された make は /home/hoge/out で実行されます。

今度は KBUILD_SRC=(Linuxソースのトップ) がセットされていますので、上記箇所は通りません。 skip-makefile には何もセットされていないので、 ifeq ($(skip-makefile),) で囲まれているMakefileの本体部分が今回は見えます。

要するに、

  • オブジェクトは make を実行したディレクトリ以下に作られる。
  • 必ずしも、Linuxのソースコードのトップで make を実行する必要はない。(その場合、Linuxのソース位置がわかりませんので、 KBUILD_SRC 変数と -f の指定が必要です)

という設計思想になっているんですね。

今回、トップのMakefileの一番外のネスト ifeq ($(skip-makefile),) 〜 について見てみました。 続きは次回



blog comments powered by Disqus