转自http://www.juliantec.info/julblog/yihect/linux-kernel-build-system-2
Linux内核构建系统所支持的目标
由前面的概述可以知道,不管是kconfig步骤、还是kbuild步骤、还是安装都可藉由"make targets"形式的命令来完成。所以,分析一下所有可能的targets是必要的。内核构建系统所支持的目标完整列表可由命令 "make help" 打印出来。这里仅简单的列出最重要的部分:
这里咱们先澄清一个观念。很多人认为内核构建系统仅仅是kbuild,其实不然。你由上面这些目标可以很清楚的看出来,整个内核构建系统实现了不仅build,而且还包括config和install。那些认为内核构建系统仅仅是 kbuild 的人,可能是受到这个词的中英文翻译的迷惑。
为了帮助大家对后面内容的理解,我们将内核构建系统所支持的所有目标分一下类。出现在 make 命令后面的内容总共有这么几个类别:
a) 和 .config 完全无关的目标
这些目标既不会产生 .config 文件,也不需要使用 .config 文件。包括上面列出来的clean,mrproper,distclean,headers_install,kernelrelease,kernelversion等等。
b) 和 .config 相关的目标
除了和.config完全无关的那些目标外,其它目标皆是要么会产生.config文件,要么需要使用.config文件。前者又称之为配置目标(config targets),后者为构建目标(build targets)。
前者主要是:config 和 %config。百分号是通配符,%config指代了一系列的以config为结尾的目标,包括menuconfig,oldconfig,silentoldconfig,s3c2410_defconfig等等。
后者主要是:all,vmlinux,modules,zImage,uImage等等。它们的产生需要以.config文件中的配置结果作为依据。
c) single targets
除了上面这几个外,上面列表中还有一类目标没有归纳进来。那就是dir/,dir/file.[ois],dir/file.ko等目标。这些目标不同于那些复合集成性的目标,它们以单个文件或目录的形式存在,比方源码树中的某一个小目录,某一个单独的对象文件,某一个外部模块文件等等。
值得一说的是,出现在make 命令后面的内容,可以是上面说到的这些目标的单独形式,也可以是这些目标的混合形式。比方我使用一个混合形式的命令 ”make oldconfig all“来既做config,又做build。
另外,make 命令后面除跟目标外,还可以跟一些变量定义作为选项,主要有这么几个:
1) V变量
make v=0 [targets] 简化编译所输出的内容 make V=1 [targets] 详细输出 make V=2 [targets] 编译过程中会编译一系列子目标,用这个选项会列出之所以编译它们的原因
2) O变量
make O=dir [target] 编译过程中,将所有中间文件和目标文件(包括.config文件)存到dir目录中, 而不是像缺省的那样放在内核源代码树中。
3) 编译外部模块时用的M变量和SUBDIRS变量
make -C KERNELDIR M=dir modules make -C KERNELDIR SUBDIRS=dir modules
其中 KERNELDIR 为内核代码树目录,dir为外部模块源代码所在目录。如果是给你自己机器上正在运行着的内核编译模块,那么KERNELDIR一般取值 /lib/modules/`uname -r`/build 。
构成内核构建系统的文件
知道内核构建系统所支持的目标之后,我们再来看看内核构建系统的组成。Linux kernel无疑是复杂的,那构建内核的工具自然也一定是相对复杂的,事实上也是如此,它由下面这一系列的文件所组成:
a) 顶层 Makefile;
提供针对各种目标的接口,一般和实现无关。当我们要针对某个目标进行分析时,作为起点,总是尝试在此文件中找到对应的目标定义,然后沿着该定义深入挖掘。
b) 平台相关的 Makefile;
提供针对不同种架构的目标,变量和规则定义。其文件位置比较固定,通常位于 arch/*/ 目录下面,即 arch/*/Makefile 文件。
c) 各类规则定义文件;
在 script 目录下面,有很多定义了不同方面规则的文件。这些文件可视作对顶层Makefile内所设计接口的实现,它们包括:
1) Kbuild.include
通用的定义。这个文件被其他Makefile所包含,比方被顶层Makeile,被Makefile.build等包含。
2) Makefile.build
这个文件定义了许多编译规则,用于编译 built-in.o, lib.a 等组件。后面会知道,正是这些组件构成了Linux内核映像。
3) Makefile.lib
这个文件给很多变量赋了值,比方obj-y,obj-m,subdir-y,subdir-m等等,这些变量所指代的一系列文件(目录)列表都是需要编译处理的。另外该文件还包括了很多cmd命令的定义,在编译内核的过程中搭配 if_changed系列使用。
4) Makefile.host
在Linux内核的编译过程但中,不仅需要使用各种诸如as,gcc,ld,tar之类的工具,而且还需要用到很多其它程序来处理源代码。比方需要用fixdep来处理目标的依赖关系,需要使用conf/gconf/mconf等工具来解析众多Kconfig配置文件中的配置选项以生成.config文件,需要使用genksyms来计算内核导出符号的校验和(checksum)等等。这些工具也以源代码的形式放在script的不同子目录下面,所以在内核编译过程中,内核构建系统需要先行将它们编译出来。那么文件Makefile.host内就定义了如何将它们编译成可执行程序的相关规则。
5) Makefile.clean
和一般应用程序的编译开发一样,内核编译后产生的结果需要经常被清除。这个文件内详细定义了做clean所需要的相关规则。
6) Makefile.headerisnt
定义了如何安装头文件的规则。
7) Makefile.modinst
前面已经说过什么是模块。其实,在我们的实际开发中,通常包含两个部分的模块。一部分是内核源代码中实际上已经实现的模块,这部分在内核构建过程中会编译出来,我们且称之为内部模块;另外一部分是作为驱动程序开发者的我们自己写出来的,通常被放在内核源码树之外目录中的模块,我们称之为外部模块,需要我们自己单独编译。文件Makefile.modinst定义了如何安装内部模块到INSTALL_MOD_PATH所指定目录中去的相关规则。外部模块一般需要靠我们自己手动拷贝到那个目录下面去。
8) Makefile.modpost
在2.6内核中,模块的编译需要两个阶段。第一阶段需要将对应的模块源代码编译成.o文件,第二阶段再将对应的.o和另外产生的.mod.o链接起来,形成.ko内核模块文件。此文件内定义的规则负责完成这第二阶段的工作。
9) Makefile.fwinst
现在要使某些设备能够正常工作的话,单单有内核里面的驱动程序是不够的,它们还需要另外一种称之为firmware的东西,这其实是一段二进制数据。在使设备能正常工作之前,设备的驱动程序需要先负责将这段数据写入设备才行。大部分firmware代码都是私有的,其所使用的License和GPL相冲突,所以内核社区里一直有激烈的争论是否要将fireware从GPLed的内核代码中去除(http://lwn.net/Articles/284932/)。在2.6.27版本之前,firmware 和驱动程序代码一起编译,但是在2.6.27版本中,驱动程序目录drivers/下的firmware被取出,单独放到firmware/目录中。此Makefile的功能是将firmware映像安装到文件系统的特定目录下,以方便驱动程序在适单的时候找到并使用它们。
d) 代码树下各目录中的Makefile
内核的编译构建是一个不断递归进入不同子目录继续编译的过程。每个子目录下面均需要有一个Makefile负责设置该目录下需要编译哪些源文件,这种设置通常取决于kconfig配置的结果。这个Makefile的名字一般就是"Makefile",但是名字“Kbuild”也可以使用,所以在内核文档Documentation/kbuild/makefiles.txt中将它们总称为"kbuild makefiles"。有时候,"Makefile"这个名字已经作为别的用途被使用掉了,那么就只好用"Kbuild"来命名。比方在arch/x86/目录下面,已经有一个”Makefile“充当架构平台相关的Makefile来使用了,那么就需要换个名字"Kbuild"。
e) 代码树各目录中的一系列配置文件 KCconfig
这些配置文件其实并不直接参与内核的构建。他们都是为了完成kconfig配置过程准备的。内核开发者事先将可以选择的选项及相关说明以一定的格式包括在这一系列KConfig中,然后在用户使用"make menuconfig"类似命令进行配置过程中,内核构建系统将这些选项从这系列配置文件中读出来呈现在屏幕上,之后用户进行操作以进行不同选项的设置。设置完最后的结果被保存在.config文件中,以供后面的kbuild过程使用。
在这众多文件中,最主要的角色自然是顶层目录下的Makefile,其他的文件都或直接、或间接的和它相关联。这些文件之间的关联可以列出如下:
如上示,我们可以将这些关联分成两类:
1) 直接包含
在一个文件中,利用include来包含另外的文件。这是比较简单的,比如字母 I,L,H 就表示了对应的文件,这里比方scripts/Makefile.build直接包含了scripts/Kbuild.include, scripts/Makefile.lib 和 scripts/Makefile.host。字母K 表示了它还包含内核树中其他子目录下的 Kbuild/Makefile 文件。
2) 使用 make -f 来调用
-f 是使用不同makefile文件来进行make的选项,请参见我们的JulWiki。这种方式在内核中用的挺多,比方上面的 Cmp, Cfi表示顶层 Makefile 调用了 scripts/Makefile.modpost 和 scripts/Makefile.fwinst。其调用的代码比方为:
有时候 "-f ... obj" 的部分被一个变量定义所取代。比方上面的 Cb,Cc,Chi 表示顶层Makefile调用了 scripts/Makefile.build, scripts/Makefile.clean 和 scripts/Makefile.headersinst,调用的代码分别举例为:
上面代码揭示了所使用的变量分别为 $(build), $(clean)和$(hdr-inst)。 而它们被定义在不同的文件里,比如 $(build) 被定义在 scripts/Kbuild.include中,具体代码为:
这种调用的方式在整个内核构建系统中非常普遍,所以需要先有个了解。实际上,变量的等号后面常跟有一个目录,而这个目录中一般都有一个Kbuild/Makefile,也就是上面所说的所谓“”各子目录下的Kbuild/Makefile”。如果你继续追踪到 scripts/Makefile.build 文件里,你会发现是她直接包含了这些”各子目录下的Kbuild/Makefile“。下面这些就是踪迹所在: