1、概述
上面简要介绍了内核Makefile的总体结构,但当我们打开顶层Makefile文件时还是因为她的复杂而觉得无从下手。但是内核Makefile就是Makefile,和最简单的Makefile遵循着同样的规则。所以只要我们静下心来分析,还是可以理解的。当然,在阅读内核的Makefile前,你必须对Makefile和shell脚本有一定的基础。
根据Makefile的执行规则,在分析Makefile时,首先必须确定一个目标 ,然后才能确定所有的依赖关系 ,最后根据更新情况决定是否执行相应的命令。所以要看懂内核Makefile的大致框架,我们首先要了解她里面所定义的目标。而内核Makefile所定义的目标基本上可以通过 make help打印出来(因为help本身就是顶层Makefile的一个目标,里面是打印帮助信息的“echo”命令)。
这些目标可以分为以下几个大类:
目标 |
常用目标举例 |
作用 |
配置 |
%config |
config |
启动Kconfig,以不同界面来配置内核。 |
menuconfig |
xconfig |
编译 |
all |
编译vmlinux内核映像和内核模块 |
vmlinux |
编译vmlinux内核映像 |
modules |
编译内核模块 |
安装 |
headers_install |
安装内核头文件/模块 |
modules_install |
源码浏览 |
tags |
生成代码浏览工具所需要的文件 |
TAGS |
cscope |
静态分析 |
checkstack |
检查并分析内核代码 |
namespacecheck |
headers_check |
内核打包 |
%pkg |
以不同的安装格式编译内核 |
文档转换 |
%doc |
把kernel文档转成不同格式 |
构架相关
(以arm为例) |
zImage |
生成压缩的内核映像 |
uImage |
生成压缩的u-boot可引导的内核映像 |
install |
安装内核映像 |
其中的构架相关目标在顶层Makefile上并未出现,而是被包含在平台相关的Makefile(arch/$(ARCH)/Makefile)中。
2、情景分析
以下我们就来分析一个简单的目标(menuconfig),作为情景分析范例来演示一下内核Makefile的分析方法。
首先当我们在内核源码的根目录下执行make menuconfig命令时,根据规则,make程序读取顶层Makefile 文件及其包含的Makefile文件,内建所有的变量、明确规则和隐含规则,并建立所有目标和依赖之间的依赖关系结构链表。make程序最终会调用规则:
config %config: scripts_basic outputmakefile FORCE
$(Q)mkdir -p include/linux include/config
$(Q)$(MAKE) $(build)=scripts/kconfig $@ |
调用的原因是我们指定的目标“menuconfig”匹配了“%config”。
她的依赖目标是scripts_basic 和outputmakefile,以及FORCE。也就是说在完成了这3个依赖目标后,下面的两个命令才会执行以完成我们指定的目标“menuconfig”。
所以我们来看看这三个依赖目标实现的简要过程:
(1)scripts_basic
make程序会调用规则:
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic |
他没有依赖目标,所以直接执行了以下的指令,只要将指令展开,我们就知道make做了什么操作。其中比较不好展开的是$(build),她的定义在scripts/Kbuild.include中:
build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj |
所以展开后是:
make -f scripts/Makefile.build obj= scripts/basic |
也就是make 解析执行scripts/Makefile.build文件,且参数obj= scripts/basic。而在解析执行scripts/Makefile.build文件的时候,scripts/Makefile.build又会通过解析传入参数来包含对应文件夹下的Makefile文件(scripts/basic/Makefile),从中获得需要编译的目标。
在确定这个目标以后,通过目标的类别来继续包含一些scripts/Makefile.*文件。例如scripts/basic/Makefile中内容如下:
hostprogs-y := fixdep docproc hash
always := $(hostprogs-y)
# fixdep is needed to compile other host programs
$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep |
所以scripts/Makefile.build会包含scripts/Makefile.host。相应的语句如下:
# Do not include host rules unless needed
ifneq ($(hostprogs-y)$(hostprogs-m),)
include scripts/Makefile.host
endif |
此外scripts/Makefile.build会包含include scripts/Makefile.lib等必须的规则定义文件,在这些文件的共同作用下完成对scripts/basic/Makefile中指定的程序编译。
由于Makefile.build的解析执行牵涉了多个Makefile.*文件,过程较为复杂,碍于篇幅无法一条一条指令的分析,兴趣的读者可以自行分析。
- 推荐两篇经典的分析文档:
- 《kbuild实现分析》 作者:x.yin@hotmail.com
- 《Kbuild系统原理分析》 作者未知,网上有PDF文档
(2)outputmakefile
make程序会调用规则:
PHONY += outputmakefile
# outputmakefile generates a Makefile in the output directory, if using a
# separate output directory. This allows convenient use of make in the
# output directory.
outputmakefile:
ifneq ($(KBUILD_SRC),)
$(Q)ln -fsn $(srctree) source
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif |
从这里我们可以看出:outputmakefile是当KBUILD_SRC 不为空(指定O=dir,编译输出目录和源代码目录分开)时,在输出目录建立Makefile时才执行命令的,
如果我们在源码根目录下执行make menuconfig命令时,这个目标是空的,什么都不做。
如果我们指定了O=dir时,就会执行源码目录下的scripts/mkmakefile,用于在指定的目录下产生一个Makefile,并可以在指定的目录下开始编译。
(3)FORCE
这是一个在内核Makefile中随处可见的伪目标,她的定义在顶层Makefile的最后:
是个完全的空目标,但是为什么要定义一个这样的空目标,并让许多目标将其作为依赖目标呢?原因如下:
正因为FORCE 是一个没有命令或者依赖目标,不可能生成相应文件的伪目标。当make执行此规则时,总会认为FORCE不存在,必须完成这个目标,所以她是一个强制目标。也就是说:规则一旦被执行,make 就认为它的目标已经被执行并更新过了。当她作为一个规则的依赖时,由于依赖总被认为被更新过的,因此作为依赖所在的规则中定义的命令总会被执行。所以可以这么说:只要执行依赖包含FORCE的目标,其目标下的命令必被执行。
在make完成了以上3个目标之后,就开始执行下面的命令的,首先是
$(Q)mkdir -p include/linux include/config |
这个很好理解,就是建立两个必须的文件夹。然后
$(Q)$(MAKE) $(build)=scripts/kconfig $@ |
这和我们上面分析的$(Q)$(MAKE) $(build)=结构相同,将其展开得到:
make -f scripts/Makefile.build obj=scripts/kconfig menuconfig |
所以这个指令的效果是使make 解析执行scripts/Makefile.build文件,且参数obj=scripts/kconfig menuconfig。这样Makefile.build会包含对应文件夹下的Makefile文件(scripts/kconfig /Makefile),并完成scripts/kconfig /Makefile下的目标:
menuconfig: $(obj)/mconf
$< $(Kconfig) |
这个目标的依赖条件是$(obj)/mconf,通过分析可知她其实是对应以下规则:
mconf-objs := mconf.o zconf.tab.o $(lxdialog)
……
ifeq ($(MAKECMDGOALS),menuconfig)
hostprogs-y += mconf
endif |
也就是编译生成本机使用的mconf程序。完成依赖目标后,通过scripts/kconfig /Makefile中对Kconfig的定义可知,最后执行:
mconf arch/$(SRCARCH)/Kconfig |
而对于conf和xconf等都有类似的过程,所以总结起来:当make %config 时,内核根目录的顶层Makefile会临时编译出scripts/kconfig 中的工具程序conf/mconf/qconf 等负责对arch/$(SRCARCH)/Kconfig 文件进行解析。这个Kconfig 又通过source标记调用各个目录下的Kconfig文件构建出一个Kconfig树,使得工具程序构建出整个内核的配置界面。在配置结束后,工具程序就会生成我们常见的.config文件。