本文简单介绍了 Linux 内核 Makefile 的大框架,对于KBuild 系统的详细的编译过程没有作过多的分析,写的很粗犷~还请见谅。
更详细的内容请参考:
嵌入式Linux应用开发完全手册
makefile.txt 这个可以参考 http://blog.csdn.net/lizuobin2/article/details/51447338
深度探索Linux操作系统:系统构建和原理解析
Linux内核中的Makefile虽然复杂,但是它的目的和作用与简单的Makefile都是一样的,无非是将文件编译,链接。它复杂在Linux内核文件太多,包括各种架构、各种设备,如何根据用户需求把它们巧妙的联系在一起,是Linux内核Makefile的难点。在Linux内核里,每个子目录都有一个makefile,它被称作Kbuilt-makefile,它将当前目录的文件编译成built-in.o、以及库文件、模块文件。然后顶层Makefile里指定这些built-in.o的路径,将它们连接在一起,具体的细节,请看下文.
Linux 内核Makefile 分类
内核makefile.txt中将makefile分为 5部分,Kernel Makefile、ARCH Makefile、KBuild Makefile、.config文件以及scripts/Makefile.*
Kernel Makefile
Kernel Makefile 位于Linux 内核源代码的顶层目录,也叫 Top Makefile 。它主要用于指定编译Linux Kernel 目标文件(vmlinux )和模块(module )路径。它根据.config文件决定了内核根目录下那些文件、子目录被编译进内核。对于内核或驱动开发人员来说,这个文件几乎不用任何修改。
ARCH Makefile
ARCH Makefile 位于ARCH/$(ARCH)/Makefile ,是系统对应平台的Makefile 。Kernel Top Makefile 会包含这个文件来指定平台相关信息。ARCH Makefile同样根据.config文件,决定了ARCH/$(ARCH) 目录下 那些文件、子目录被编译进内核 只有平台开发人员会关心这个文件。
Kbuild Makefile
从Linux 内核2.6 开始,Linux 内核的编译采用Kbuild 系统 ,这同过去的编译系统有很大的不同,Kbuild 系统使用Kbuild Makefile 来编译内核或模块。当Kernel Makefile 被解析完成后,Kbuild 会读取相关的Kbuild Makefile 进行内核或模块的编译。Kbuild Makefile 有特定的语法指定哪些编译进内核中、哪些编译为模块、及对应的源文件是什么等。内核及驱动开发人员需要编写这个Kbuild Makefile 文件。
scripts/Makefile.* 通用规则
Makefile.build
被顶层Makefile所调用,与各级子目录的Makefile合起来构成一个完整的Makefile文件,定义built-in.o、.lib以及目标文件.o的生成规则。这个Makefile文件生成了子目录的.lib、built-in.o以及目标文件.o
Makefile.clean
被顶层Makefile所调用,用来删除目标文件等
Makefile.lib
被Makefile.build所调用,主要是对一些变量的处理,比如说在obj-y前边加上obj目录
Kbuild.include
被Makefile.build所调用,定义了一些函数,如if_changed、if_changed_rule、echo-cmd
详细分析请参考:深度探索Linux操作系统:系统构建和原理解析
.config
来自配置过程,生成 auto.conf 以及 autoconf.h,被顶层Makefile所包含,参考http://blog.csdn.net/lizuobin2/article/details/51429937
Kbuild Makefile
内核虽然有自己的构建系统Kbuild,但是kbuild 并不是什么新的东西,我们可以把kbuild 看作利用GNU Make组织的一套复杂的构建系统,虽然kbuild 也在Make 基础上做了适当的扩展,但是因为内核的复杂性,所以kbuild 要比一般的项目的Makefile 的组织要复杂的多。为了方便Linux开发者能够编写Makefile,kbuild考虑得不可谓不周到,比如,kbuild将所有与编译过程相关的共用规则和变量都提取到scripts 目录下的Makefile.build中,而具体的子目录下的 Makefile 文件则可以编写的非常简单与直接。
大多的Kbuild 文件的名字都是Makefile 。为了与其他Makefile 文件相区别,你也可以指定Kbuild Makefile 的名字为 Kbuild 。而且如果“Makefile ”和“Kbuild ”文件同时存在,则Kbuild 系统会使用“Kbuild ”文件。在这里,我们的 Kbuild Makefile 就是各个子目录的Makefile ,它的作用就是 指定当前目录下的文件,哪些被编译进当前目录的built-in.o、那些被编译成模块、那些不编译。
Kbuild Makefile 的一个最主要功能就是指定编译什么,这个功能是通过下面两个对象指定的obj-? 和xxx-objs
例如:
obj-y += foo.o
obj-m += abc.o
大多数情况下是这样:
obj-$(CONFIG_DM9000) += dm9000.o
具体的 CONFIG_DM9000 是y 还是m取决于配置过程,配置过程产生.config文件,.config又产生auto.conf以及autoconf.h文件,包含在makefile文件里。
参考:http://blog.csdn.net/lizuobin2/article/details/51429937
如果内核模块是通过几个源文件编译而成的,您可以使用和上面同样的方法指定您想要编译的模块.然而此时Kbuild需要知道编译模块时是基于那些目标文件的,因此您需要设置一个$(<module_name>-objs)变量来告诉编译器.
例如:#drivers/isdn/i4l/Makefile
obj-$(CONFIG_ISDN) += isdn.o
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
用 obj-* 连接的Objects 在指明的文件夹中被用作模块或者综合进built-in.o 。也又可能被列出的objects 将会被包含进一个库,lib.a 。所有用lib-y 列出的objects 在那个文件夹中被综合进单独的一个库。列在obj-y 且 附加列在lib-y 中的Objects 将不会被包含在库中,因为他们将会被任意的存取。对于被连接在lib-m 中,连续的objects 将会被包含在lib.a 中。值得注意的是kbuild makefile 可能列出文件用作built-in ,并且作为库的一部分。因此,同一个文件夹可能包含一个built-in.o 和lib.a 文件
例如:
lib-y := checksum.o delay.o
这里讲会创建一个基于checksum.o 和delay.o 的库文件。对于kbuild ,识别一个lib.a 正在被构建,这个文件夹应该被列在 libs-y 中。 lib-y 的使用方法通常被限制在lib/ 和arc/*/lib 中。
Kernel Makefile
/* 指定 平台、编译器 */
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-
/* 设置 6 类文件编译路径 */
init-y := init/
drivers-y := drivers/ sound/ firmware/
net-y := net/
libs-y := lib/
core-y := usr/
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
/* 包含 ARCH Makefile 在通用编译路径基础上增加 架构相关的路径 */
include $(srctree)/arch/$(SRCARCH)/Makefile
head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
core-y += $(machdirs) $(platdirs)
core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/
core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ)
core-$(CONFIG_VFP) += arch/arm/vfp/
drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/
libs-y := arch/arm/lib/ $(libs-y)
/* 指定路径下的built-in.o 文件 */
init-y := $(patsubst %/, %/built-in.o, $(init-y))
core-y := $(patsubst %/, %/built-in.o, $(core-y))
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
net-y := $(patsubst %/, %/built-in.o, $(net-y))
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y := $(libs-y1) $(libs-y2)
$(patsubst A, AB, C) 会将AB中的A替换为C
例如:
net-y := $(patsubst %/, %/built-in.o, $(net-y))
$(net-y) == net/
将 %/built-in.o中的%/替换为net/
结果为:net/built-in.o
/* 最终链接成vmlinux-all */
vmlinux-init := $(head-y) $(init-y)
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux-all := $(vmlinux-init) $(vmlinux-main)
vmlinux-lds := arch/$(SRCARCH)/kernel/vmlinux.lds
export KBUILD_VMLINUX_OBJS := $(vmlinux-all)
根据 vmlinux-all 我们可以得出文件的排放顺序,
head-y 路径下的built-in.o
init-y 路径下的built-in.o
core-y 路径下的built-in.o
libs-y 路径下的built-in.o
drivers-y 路径下的built-in.o
net-y 路径下的built-in.o