2012年2月12日日曜日

LinuxカーネルのMakefileを解析する その2

前回は、LinuxのトップのMakefile の一番外のネスト
ifeq ($(skip-makefile),)
について説明しました。

その内部にさらにわかりにくい部分があります。

 トップのMakefile の400行目付近からは以下のようなコードになっています。


# To make sure we do not include .config for any of the *config targets
# catch them early, and hand them over to scripts/kconfig/Makefile
# It is allowed to specify more targets when calling make, including
# mixing *config targets and build targets.
# For example 'make oldconfig all'.
# Detect when mixed targets is specified, and make a second invocation
# of make so .config is not included in this case either (for *config).

no-dot-config-targets := clean mrproper distclean \
             cscope gtags TAGS tags help %docs check% coccicheck \
             include/linux/version.h headers_% \
             kernelversion %src-pkg

config-targets := 0
mixed-targets  := 0
dot-config     := 1

ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
    ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
        dot-config := 0
    endif
endif

ifeq ($(KBUILD_EXTMOD),)
        ifneq ($(filter config %config,$(MAKECMDGOALS)),)
                config-targets := 1
                ifneq ($(filter-out config %config,$(MAKECMDGOALS)),)
                        mixed-targets := 1
                endif
        endif
endif



Linuxのビルドにはまず、.configファイルを作らないといけません。
いきなり
$ make
とやっても .config ファイルがないよと怒られます。

普通は2回に分けて、
$ make oldconfig
$ make all
などとするところなのですが、
$ make oldconfig all
と1回でもうまくいきます。そのあたりの仕組みを説明します。

まず、makeのターゲットを3種類に分類しています。

(1) .configファイルを生成するためのターゲット (config / menuconfig / defconfig/ oldconfig など)
(2) .config がなくても実行できるターゲット (clean / mrproper / help など)
(3) .config がないと実行できないターゲット (all / vmlinux / modules など)


ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
    ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
        dot-config := 0
    endif
endif

の部分ですが、no-dot-config-targetsには(2)の.configファイルを必要としないターゲットが列挙されています。
MAKECMDGOALS が(2) のターゲットのみからなる場合、
dot-config := 0がセットされます。(.config ファイルはなくても実行できますよ、という意味。)
それ以外の場合は dot-config := 1です。

ifeq ($(KBUILD_EXTMOD),)
        ifneq ($(filter config %config,$(MAKECMDGOALS)),)
                config-targets := 1
                ifneq ($(filter-out config %config,$(MAKECMDGOALS)),)
                        mixed-targets := 1
                endif
        endif
endif

の部分ですが、MAKECMDGOALSが (1) .configファイルを生成するターゲットを含んでいる場合は
config-targets := 1
になります。
さらに、MAKECMDGOALSに (1)と(1)以外のターゲットが混ざっている場合は
mixed-targets := 1
になります。

dot-config, config-targets, mixed-targets の値によって、Makefileは以下のように分岐します。



ifeq ($(mixed-targets),1)
# ===========================================================================
# We're called with mixed targets (*config and build targets).
# Handle them one by one.

%:: FORCE
    $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@

else
ifeq ($(config-targets),1)
# ===========================================================================
# *config targets only - make sure prerequisites are updated, and descend
# in scripts/kconfig to make the *config target
   ...

config: scripts_basic outputmakefile FORCE
    $(Q)mkdir -p include/linux include/config
    $(Q)$(MAKE) $(build)=scripts/kconfig $@

%config: scripts_basic outputmakefile FORCE
    $(Q)mkdir -p include/linux include/config
    $(Q)$(MAKE) $(build)=scripts/kconfig $@

else
# ===========================================================================
# Build targets only - this includes vmlinux, arch specific targets, clean
# targets and others. In general all targets except *config targets.

...

ifeq ($(dot-config),1)
# Read in config
-include include/config/auto.conf

...

# If .config is newer than include/config/auto.conf, someone tinkered
# with it and forgot to run make oldconfig.
# if auto.conf.cmd is missing then we are probably in a cleaned tree so
# we execute the config step to be sure to catch updated Kconfig files
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
    $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig

   ...

else
# Dummy target needed, because used as prerequisite
include/config/auto.conf: ;
endif # $(dot-config)

  ...





mixed-target が1の時は、

%:: FORCE
    $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@

の部分しか見えませんが、これは1つ1つに分けて再帰的に呼び出しています。

$ make oldconfig all
は make oldconfig と make all の2回に分けて実行されるということです。


mixed-target が0で、config-targets が1のときは

config: scripts_basic outputmakefile FORCE
    $(Q)mkdir -p include/linux include/config
    $(Q)$(MAKE) $(build)=scripts/kconfig $@

%config: scripts_basic outputmakefile FORCE
    $(Q)mkdir -p include/linux include/config
    $(Q)$(MAKE) $(build)=scripts/kconfig $@


の部分が、*configターゲット の生成ルールになっています。


mixed-target が0で、config-targets が0のときが、それ以降の大部分のコードになります。

dot-config が1ならば、

-include include/config/auto.conf

のようにCONFIG_設定ファイルを読み込んでいます。
.config を直接 include するのではなくて、
make silentoldconfigによって .config から include/config/auto.conf を作り、それを読み込んでいます。

dot-config が0ならば、
include/config/auto.conf は必要ないのですが、空のルールだけは、
include/config/auto.conf: ;
のように書いてあります。
include/config/auto.conf がなくてもそのまま makeは進みます。

前回に引き続き、今回もトップのMakefileのわかりにくいネストを説明しました。

インデントをつけてわかりやすくまとめますと、

ifeq ($(mixed-targets),1)
  再帰的に1つずつ処理する
else
  ifeq ($(config-targets),1)
    .configを作る
  else
    ifeq ($(dot-config),1)
      include/config/auto.confを読み込む
    endif

    本体
  endif
endif

といった感じです。

0 件のコメント:

コメントを投稿