顶层Makefile的总体框架结构

 

http://blog.csdn.net/unbutun/article/details/6445972

 

顶层Makefile的总体框架结构  

既然前面我们说过顶层Makefile最为重要,那么我们就先来研究一下这个文件。在你用VI编辑器打开这个文件时,千万别被它的复杂吓倒。这个文件虽然行数颇多,但其实里面也是有道道可寻的,我们可以抽出其中最重要的框架结构出来,列出如下(稍做整理和缩进):

Linux内核顶层Makefile文件整体框架

从上面的框架中可以看出,影响内核构建过程动作的有数个变量,分别是:KBUILD_SRC, KBUILD_OUTPUT, skip-makefile, mixed-targets, config-targets 和 dot-config。我们将它们分成两组,前三为一组,后三个为一组。显然,前一组影响着框架中最外面的两个ifeq-endif块,而后一组则决定了第二个ifeq-endif块内的逻辑。

对于第一组变量,其实是为了支持"make O=1 [Targets]", 也就是为了支持将输出文件放到另外目录(不同于内核源代码目录的其他目录)的功能而准备的。那到底是如何支持的呢?我们说其实这是两次调用顶层Makefile的过程。

首先,当我们输入"make O=dir [Targets]"命令的时候,会第一次调用顶层Makefile。而此时变量KBUILD_SRC没有定义,所以会进入到第一个ifeq-endif块。进去之后,条件判断ifeq ("$(origin O)", "command line")会发现命令行里有变量O的定义,并且其值为dir。所以接下来,make会把变量O的值,也就是dir赋给变量KBUILD_OUTPUT。

既然KBUILD_OUTPUT的值为dir,所以条件判断ifneq ($(KBUILD_OUTPUT),)就必定成立,所以make会去处理上面的A部分。处理完A部分后,会将变量skip-makefile设置为1。因此显然,在退出第一个ifeq-endif块后,进入第二个ifeq-endif块的条件判断就得不到满足。所以我们说,其实到这里,第一次调用Makefile的过程就提前结束了。

那第二次调用顶层Makefile的过程又是在哪里呢?答案是在对A部分的处理上。我们列出A部分中关键的代码:

 

PHONY += $<span class="br0">(</span>MAKECMDGOALS<span class="br0">)</span> sub-make
 
$<span class="br0">(</span>filter-out _all sub-make $<span class="br0">(</span>CURDIR<span class="br0">)</span>/Makefile, $<span class="br0">(</span>MAKECMDGOALS<span class="br0">)</span><span class="br0">)</span> _all: sub-make
        $<span class="br0">(</span>Q<span class="br0">)</span>@:
 
sub-make: FORCE
        $<span class="br0">(</span><span class="kw1">if</span> $<span class="br0">(</span>KBUILD_VERBOSE:<span class="nu0">1</span>=<span class="br0">)</span>,@<span class="br0">)</span>$<span class="br0">(</span>MAKE<span class="br0">)</span> -C $<span class="br0">(</span>KBUILD_OUTPUT<span class="br0">)</span> /
        KBUILD_SRC=$<span class="br0">(</span>CURDIR<span class="br0">)</span> /
        KBUILD_EXTMOD=<span class="st0">"$(KBUILD_EXTMOD)"</span> -f $<span class="br0">(</span>CURDIR<span class="br0">)</span>/Makefile /
        $<span class="br0">(</span>filter-out _all sub-make,$<span class="br0">(</span>MAKECMDGOALS<span class="br0">)</span><span class="br0">)</span>

 

从这里,我们可以看出,不管命令"make O=dir [Targets]"中的Targets个数有多少个,它们都是要依赖于 sub-make。所以,对A部分的处理,其实就是执行sub-make规则的命令。在这个命令中,KBUILD_OUTPUT值为dir,CURDIR值为当前的内核源码目录。因为make命令中没有设置M变量或者SUBDIRS变量,所以KBUILD_EXTMOD值为空。所以这个命令就简化为:

make -C dir KBUILD_SRC=`pwd` KBUILD_EXTMOD="" -f `pwd`/Makefile [Targets]

这样一个命令的执行就和执行没带O=dir的"make [Targets]"命令差不多了。其差别只在于将输出文件存到dir目录,而非内核源码树目录而已。

从上面的框架中看出来,如果只是简单的"make [Targets]",那么就不会进去到第一个ifeq-endif块,而直接进到第二个ifeq-endif块中去处理了。这就要涉及到上面说到的第二组变量了。我们说,设置第二组变量的目的,就是为了适应make命令后面所跟Targets数目和类型上的多样性而已。

在详细分析第二个ifeq-endif块之前,让我们看看第二组变量的含义以及它们的初始值设置情况。由于我们之前已经对几乎所有Targets的作用及分类做了说明,所以理解它们不应该很难。如上面框架图中所示的那样,让我们先抽出B2部分中和这个相关的代码如下:

 

no-dot-config-targets := clean mrproper distclean /
                         cscope TAGS tags help %docs check% /
                         include/linux/version.<span class="me1">h</span> headers_% /
                         kernelrelease kernelversion
 
config-targets := <span class="nu0">0</span>
mixed-targets  := <span class="nu0">0</span>
dot-config     := <span class="nu0">1</span>
 
ifneq <span class="br0">(</span>$<span class="br0">(</span>filter $<span class="br0">(</span>no-dot-config-targets<span class="br0">)</span>, $<span class="br0">(</span>MAKECMDGOALS<span class="br0">)</span><span class="br0">)</span>,<span class="br0">)</span>
        ifeq <span class="br0">(</span>$<span class="br0">(</span>filter-out $<span class="br0">(</span>no-dot-config-targets<span class="br0">)</span>, $<span class="br0">(</span>MAKECMDGOALS<span class="br0">)</span><span class="br0">)</span>,<span class="br0">)</span>
                dot-config := <span class="nu0">0</span>
        endif
endif
 
ifeq <span class="br0">(</span>$<span class="br0">(</span>KBUILD_EXTMOD<span class="br0">)</span>,<span class="br0">)</span>
        ifneq <span class="br0">(</span>$<span class="br0">(</span>filter config %config,$<span class="br0">(</span>MAKECMDGOALS<span class="br0">)</span><span class="br0">)</span>,<span class="br0">)</span>
                config-targets := <span class="nu0">1</span>
                ifneq <span class="br0">(</span>$<span class="br0">(</span>filter-out config %config,$<span class="br0">(</span>MAKECMDGOALS<span class="br0">)</span><span class="br0">)</span>,<span class="br0">)</span>
                        mixed-targets := <span class="nu0">1</span>
                endif
        endif
endif

 

上面的代码一开始就设置了变量no-dot-config-targets,它指代的是那些和.config没有关系的目标,这已经在前面对目标进行分类的时候说过了。接下来,它将三个变量分别赋初值0,0和1。再接下来,它会判断make命令中的[Targets]部分是否有且仅有变量no-dot-config-targets所指代的那些目标,如果是的话它就将变量dot-config的值设置为0。这表明本次make命令所make的目标都是和.config文件没有关联的,既不是那些会产生.config的配置目标,也不是那些需要使用.config文件内容的构建目标。

好,上面的条件判断ifeq ($(KBUILD_EXTMOD),)指示了如果我们不是编译外部模块的话就进去到这最后一个ifeq-endif块里面。进去后,构建系统如果发现make命令中[Targets]部分包含有config,或者有%config目标的话,它就将变量config-targets设置为1,这表明本次make需要处理配置目标。在此基础之上,如果它还发现有其他目标的话,它就将mixed-targets变量设置为1。注意mixed-targets变量的确切含义,其确切含义是指配置目标和其他目标相混合,而不是仅仅指配置目标和构建目标相混合。换句话讲,对于"make s3c2410_defconfig kernelversion"这样的命令来说,配置目标s3c2410_defconfig和与.config文件不相关的目标kernelversion相混合,此时变量mixed-targets也会被设置为1。

讨论完B2部分对三个变量的初始化,让我们再回到主框架结构上面来。框架结构中接下来就是针对三变量不同的取值搭配进行处理了。

首先,如果mixed-targets取值为1,则表明是混合目标的情况,构建系统要处理框架中的C部分。我们取出其中代码如下:

 

# ===========================================================================
# We<span class="st0">'re called with mixed targets (*config and build targets).
# Handle them one by one.
 
%:: FORCE
        $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@
 </span>

 

从代码中可以看出,这里使用了一个双冒号的模式匹配规则。百分号代表任何目标都使用这个规则,其中$(srctree)为内核代码树所在目录,KBUILD_SRC定义为空。所以如果make命令为:make s3c2410_defconfig all,那么构建系统就会分别执行下面两条命令:

 

	make -C $<span class="br0">(</span>srctree<span class="br0">)</span> KBUILD_SRC= s3c2410_defconfig
	make -C $<span class="br0">(</span>srctree<span class="br0">)</span> KBUILD_SRC= all

 

这其实和简单的用手动的输入两条连续命令(make s3c2410_defconfig 和 make all)是一样效果的。

回到主框架,如果make命令的[Targets]部分不是混合目标,而是单个目标。那么构建系统会先用ifeq ($(config-targets),1)判断是否是配置目标,如果是,则处理框架中的D部分,我们也抽出D部分的代码如下:

 

# ===========================================================================
# *config targets only - make sure prerequisites are updated, and descend
# in scripts/kconfig to make the *config target
 
# Read arch specific Makefile to set KBUILD_DEFCONFIG as needed.
# KBUILD_DEFCONFIG may point out an alternative <span class="kw1">default</span> configuration
# used <span class="kw1">for</span> <span class="st0">'make defconfig'</span>
include $<span class="br0">(</span>srctree<span class="br0">)</span>/arch/$<span class="br0">(</span>SRCARCH<span class="br0">)</span>/Makefile
export KBUILD_DEFCONFIG KBUILD_KCONFIG
 
config: scripts_basic outputmakefile FORCE
        $<span class="br0">(</span>Q<span class="br0">)</span>mkdir -p include/linux include/config
        $<span class="br0">(</span>Q<span class="br0">)</span>$<span class="br0">(</span>MAKE<span class="br0">)</span> $<span class="br0">(</span>build<span class="br0">)</span>=scripts/kconfig $@
 
%config: scripts_basic outputmakefile FORCE
        $<span class="br0">(</span>Q<span class="br0">)</span>mkdir -p include/linux include/config
        $<span class="br0">(</span>Q<span class="br0">)</span>$<span class="br0">(</span>MAKE<span class="br0">)</span> $<span class="br0">(</span>build<span class="br0">)</span>=scripts/kconfig $@

 

观察上面D部分的代码,无论配置目标是config,还是%config形式的,都依赖于 script_basic 和 outputmakefile两个目标。我们可以找到这两个依赖是定义在框架B1部分中的,抽出代码如下:

 

# ===========================================================================
# Rules shared between *config targets and build targets
 
# Basic helpers built in scripts/
PHONY += scripts_basic
scripts_basic:
        $<span class="br0">(</span>Q<span class="br0">)</span>$<span class="br0">(</span>MAKE<span class="br0">)</span> $<span class="br0">(</span>build<span class="br0">)</span>=scripts/basic
 
# To avoid any implicit rule to kick in, define an empty command.
<span class="me1">scripts</span>/basic/%: scripts_basic ;
 
PHONY += outputmakefile
# outputmakefile generates a Makefile in the output directory, <span class="kw1">if</span> using a
# separate output directory. <span class="me1">This</span> allows convenient use of make in the
# output directory.
<span class="me1">outputmakefile</span>:
ifneq <span class="br0">(</span>$<span class="br0">(</span>KBUILD_SRC<span class="br0">)</span>,<span class="br0">)</span>
        $<span class="br0">(</span>Q<span class="br0">)</span>ln -fsn $<span class="br0">(</span>srctree<span class="br0">)</span> source
        $<span class="br0">(</span>Q<span class="br0">)</span>$<span class="br0">(</span>CONFIG_SHELL<span class="br0">)</span> $<span class="br0">(</span>srctree<span class="br0">)</span>/scripts/mkmakefile /
            $<span class="br0">(</span>srctree<span class="br0">)</span> $<span class="br0">(</span>objtree<span class="br0">)</span> $<span class="br0">(</span>VERSION<span class="br0">)</span> $<span class="br0">(</span>PATCHLEVEL<span class="br0">)</span>
endif

 

正如上面代码中的注释所说的一样,这两个目标对应的规则其实是Kconfig和Kbuild都要用到的。scripts_basic规则的命令 $(Q)$(MAKE) $(build)=scripts/basic 做的工作就是:make -f scripts/Makefile.build obj=scripts/basic ,为什么?回过头去看看我们前面说的构建系统内各makefile的关联就知道了。

make -f scripts/Makefile.build obj=scripts/basic 命令由于没有指定目标,所以会在 script/Makefile.build 中处理默认目标__build,如下:

 

 
__build: $<span class="br0">(</span><span class="kw1">if</span> $<span class="br0">(</span>KBUILD_BUILTIN<span class="br0">)</span>,$<span class="br0">(</span>builtin-target<span class="br0">)</span> $<span class="br0">(</span>lib-target<span class="br0">)</span> $<span class="br0">(</span>extra-y<span class="br0">)</span><span class="br0">)</span> /
         $<span class="br0">(</span><span class="kw1">if</span> $<span class="br0">(</span>KBUILD_MODULES<span class="br0">)</span>,$<span class="br0">(</span>obj-m<span class="br0">)</span> $<span class="br0">(</span>modorder-target<span class="br0">)</span><span class="br0">)</span> /
         $<span class="br0">(</span>subdir-ym<span class="br0">)</span> $<span class="br0">(</span>always<span class="br0">)</span>
        @:

 

同时,别忘记在scripts/Makefile.build中会包含进 scripts/basic 目录下的 Kbuild/Makefile,所以该make命令的实际效果是去编译出 scripts/basic 目录下的三个 host program,也就是 fixdep docproc和hash。什么是host program?一般认为是和内核无关,但是要在编译过程中使用的工具程序。关于这些程序的编译,请参见 scripts/Makefile.host 文件,以及Documentation/kbuild/makefile.txt 文件中关于 host program的这一节。请你留意这里的变量$(always)。

对于目标outputmakefile,其实只在 make 命令带有O 变量时才有用,因为这个时候变量 KBUILD_SRC 不会为空。这个时候中间文件不会存在内核源码树目录中,而是存在另外的一个目录。这个没什么困难的,所以我们这里不再做过多说明。

好,回到我们对配置目标的处理上面来。在处理完两个依赖后,构建系统将会创建两个目录: include/linux 和 include/config。接着执行 $(Q)$(MAKE) $(build)=scripts/kconfig $@ 。如果我们的make 命令是 "make s3c2410_defconfig" 的话,这个时候执行的就是:

make -f scripts/Makefile.build obj=scripts/kconfig s3c2410_defconfig

又牵涉到文件 scripts/Makefile.build 了,我们先搜索一下这个文件里面有没有 s3c2410_defconfig 或 类似于 %config 之类的目标。没有,怎么办,该不会是弄错了吧?呵呵,你忘记了么?文件 scripts/Makefile.build 会包含obj变量所指代目录内的 Makefile的,在这里就是 script/kconfig/Makefile。打开这个文件,果然能找到相关的规则:

Linux内核构建系统中defconfig系列目标的处理

在这里,s3c2410_defconfig 需要依赖于同目录下的conf程序。这其实就是Linux内核进行Kconfig操作的主程序之一了,类似的还有mconf,qconf和gconf等。他们其实都是host program。关于它们是如何被编译出来的,还请参见 scripts/kconfig/Makefile 文件,主要是借助于bison,flex和gperf三个工具来生成c源程序文件,之后再编译出来的。由于这部分和我们Linux内核的构建主题关系不大,所以我在这里不再赘述。

由于变量 KBUILD_KCONFIG 在arm架构Makefile中没有被定义,所以 Kconfig 被定义成 arch/arm/kconfig,所以这个目标的规则就简化成:

$(obj)/conf -D arch/arm/configs/s3c2410_defconfig arch/arm/Kconfig

这个命令就是读取并解析以 arch/arm/Kconfig 为首的内核功能选项配置文件,并将文件 arch/arm/configs/s3c2410_defconfig 所设置的默认值分配给对应的所有选项,最终生成隐藏配置文件 .config 。你可以打开看一下 .config 文件的内容,都是这样的格式:

 

#
# Automatically generated make config: don<span class="st0">'t edit
# Linux kernel version: 2.6.31
# Tue Nov 30 21:03:12 2010
#
CONFIG_ARM=y
CONFIG_HAVE_PWM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
CONFIG_GENERIC_GPIO=y
CONFIG_MMU=y
CONFIG_NO_IOPORT=y
CONFIG_GENERIC_HARDIRQS=y
CONFIG_STACKTRACE_SUPPORT=y
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
CONFIG_LOCKDEP_SUPPORT=y
.....
 </span>

 

里面貌似都是一些变量的定义。稍后我们会看到在内核开始真正编译之前,构建系统会以 .config 文件为蓝本生成 include/config/auto.conf 文件,这个文件的格式和 .config类似,这个文件会在顶层 以及 scripts/Makefile.build 文件中被直接包含进来,所以这些变量其实就成了 GNU Make 的变量。而内核各子目录中的 Kbuild/Makefile 就可以使用这些变量的定义,来决定是否将该目录下对应的代码功能直接编译到内核里面(这些变量取值为"y")、编译成模块(取值为"m")或者干脆不进行编译(取值为"空")。可以想见,如果选择不编译,那出来的Linux内核就不会有对应的功能。

前面之所以说是以 arch/arm/Kconfig 为首的,那就说明功能配置选项文件可能有多个。的确如此,你如果打开文件 arch/arm/Kconfig 就会看到它通过 source 来包含其他的选项配置文件:

Linux内核构建系统中所使用的配置选项文件

不光是 arm 架构这样,其他所有的架构也都这样。在配置的时候,配置工具首先会解析架构平台目录下的 Kconfig,这就是所谓和平台相关的主Kconfig。主Kconfig文件会包含其他目录的Kcofnig文件,而其他目录的Kconfig又会包含其他各子目录的 Kconfig。如此形成一个树型结构。

另外需要说一下的,如果你的make 命令是 "make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig",那用到的配置工具将会是 mconf,它同样会读取那些配置选项文件,只不过不同的是,它会show除一个菜单界面,并将这些可设置的选项一一呈现出来,如此我们就可以手动进行设置,而不是让mconf去读取 arch/arm/configs/s3c2410_defconfig 之类的文件进行默认设置了。关于gconf等其他配置工具以及Kconfig文件中具体的选项配置语法,我这里就不再详细叙述了,你可以参考目录 Documentation/kbuild/ 下的文件 kconfig.txt以及 kconfig-language.txt 。关于配置目标,除了这里的 menuconfig 和 s3c2410_defconfig,我们后面还会说到另外一个很重要的配置目标: silentoldconfig 。

好,做个阶段性总结吧。作为内核构建系统对 kconfig 的支持,到这步就算是结束了,其根本目标是产生 .config 隐藏文件,用以记录我们所需要的配置结果。但是在Linux内核里面,仅仅把配置结果保存在像 .config 这样一个文件中是不够的。为什么这么说?我们说辛苦辛苦保存起来的东西在关键时候总是需要派上用场的。那么在Linux系统中,这些配置结果由谁去用,又是怎么去用呢?我们在后面会给大家一一道来,其实关于由谁去用,我们前面已经稍微提到过一点了。这里我们还是先不去管这些,休息片刻后把注意力转移到我们的主框架上面来吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值