如果需要将一个模块配置进内核,需要在makefile中进行配置:
obj-y += disp.o
将disp.o编译进内核,根据make的自动推导原则,make将会自动将disp.c编译成disp.o。该方法为编译单独模块。 当编译整个内核时,在top makefile中这样写:
obj-$(CONFIG_DISP2_SUNXI) += disp.o
.config文件中将CONFIG_DISP2_SUNXI变量配置为y,要修改模块编译行为时,可在配置文件中修改,而不用到makefile去找。
在一个目录下,obj-y所列出的文件,将被编译成built-in.o文件。而lib-y或lib-m所列出的文件,将在当前目录下生成lib.a文件。 (注意:一般lib-y或lib-m只用在lib/和arch/*/lib这两个目录中)。
$(CONFIG_DISP2_SUNXI) 是一个整体,$(CONFIG_DISP2_SUNXI)表示引用变量 CONFIG_DISP2_SUNXI。比如定义 CONFIG_DISP2_SUNXI=y;$(CONFIG_DISP2_SUNXI)就是y;obj-$(CONFIG_DISP2_SUNXI) 就是 obj-y。
配置文件中标记为-m的模块将被编译成可加载模块.ko文件。
如果需要将一个模块配置为可加载模块,需要在makefile中进行配置:
obj-m += disp.o
同样的,通常可以写成这样的形式:
obj-$(CONFIG_DISP2_SUNXI) += disp.o
在.config文件中将CONFIG_DISP2_SUNXI变量配置成m,在配置文件中统一控制,编译完成时将会在当前文件夹中生成disp.ko文件,在内核运行时使用insmod或者是modprobe指令加载到内核。
单独编译自己开发的驱动模块,当一个驱动模块依赖多个源文件时,需要通过以下方式来指定依赖的文件:
obj-m += disp.o
disp-y := a.o b.o c.o
disp.o 由a.o,b.o,c.o生成,然后调用$(LD) -r 将a.o,b.o,c.o链接成disp.o文件。
同样地,makefile支持以变量的形式来指定是否生成disp.o,我们可以这样:
obj-$(CONFIG_DISP2_SUNXI) += disp.o
disp-$(CONFIG_EINK_PANEL_USED) += de/disp_eink_manager.o de/eink_pipeline_manager.o
根据CONFIG_FOO_XATTR的配置属性来决定是否生成disp.o,然后根据CONFIG_DISP2_SUNXI属性来决定将disp.o模块编入内核还是作为模块。
obj-y = disp.o
disp-objs := dev_disp.o dev_disp_debugfs.o
将dev_disp.c dev_disp_debugfs.c两个文件编译后链接生成disp.o
一个原则就是:一个makefile只负责处理本目录中的编译关系,自然地,其他目录中的文件编译由其他目录的makefile负责,整个linux内核的makefile组成一个树状结构,对于上层makefile的子目录而言,只需要让kbuild知道它应该怎样进行递归地进入目录即可。
kbuild利用目录指定的方式来进行目录指定操作,举个例子:
obj-$(CONFIG_DISP2_SUNXI) += de/
当CONFIG_DISP2_SUNXI被配置成y或者m时,kbuild就会进入到de/目录中,但是需要注意的是,这个信息仅仅是告诉kbuild应该进入到哪个目录,而不对其目录中的编译做任何指导。kbuild系统会在de目录中调用make命令(即de目录中的Makefile)
参考如下:
obj-$(CONFIG_DISP2_SUNXI) += disp.o
disp-objs := dev_disp.o disp_sys_intf.o
disp-objs += de/disp_display.o de/disp_features.o de/disp_device.o de/disp_lcd.o de/disp_manager.o de/disp_private.o \
de/disp_smart_backlight.o de/disp_enhance.o de/disp_capture.o de/disp_hdmi.o de/disp_tv.o de/disp_vga.o de/disp_vdevice.o \
de/disp_edp.o
disp-$(CONFIG_EINK_PANEL_USED) += de/disp_eink_manager.o de/eink_pipeline_manager.o
disp-objs += lcd/panels.o lcd/lcd_source.o lcd/default_panel.o
disp-$(CONFIG_LCD_SUPPORT_HE0801A068) += lcd/he0801a068.o
disp-$(CONFIG_EINK_PANEL_USED) += lcd/default_eink.o
disp-$(CONFIG_LCD_SUPPORT_LT070ME05000) += lcd/lt070me05000.o
CHK include/config.h
CFG u-boot.cfg
CHK include/generated/version_autogenerated.h
CHK include/generated/generic-asm-offsets.h
CHK include/generated/asm-offsets.h
HOSTCC tools/mkenvimage.o
HOSTCC tools/fit_image.o
HOSTCC tools/image-host.o
HOSTCC tools/dumpimage.o
HOSTCC tools/mkimage.o
HOSTCC tools/sunxi-spl-image-builder.o
HOSTLD tools/mkenvimage
HOSTLD tools/sunxi-spl-image-builder
HOSTLD tools/dumpimage
HOSTLD tools/mkimage
CC board/sunxi/sunxi_bootargs.o
CC cmd/version.o
CC common/main.o
LD cmd/built-in.o
LD common/built-in.o
LD board/sunxi/built-in.o
CC drivers/video/sunxi/disp2/disp/lcd/panels.o
CC drivers/video/sunxi/disp2/disp/lcd/lcd_source.o
CC drivers/video/sunxi/disp2/disp/lcd/default_panel.o
CC drivers/video/sunxi/disp2/disp/lcd/he0801a068.o
CC drivers/video/sunxi/disp2/disp/lcd/bp101wx1-206.o
LD drivers/video/sunxi/disp2/disp/disp.o
LD drivers/video/sunxi/disp2/disp/built-in.o
LD drivers/video/sunxi/disp2/built-in.o
LD drivers/video/sunxi/built-in.o
LD drivers/video/built-in.o
LD drivers/built-in.o
CC lib/display_options.o
LD lib/built-in.o
LD u-boot
OBJCOPY u-boot.srec
OBJCOPY u-boot-nodtb.bin
在Linux内核里,每个子目录都有一个makefile,它被称作Kbuilt-makefile,它将当前目录的文件编译成built-in.o、以及库文件、模块文件。然后顶层Makefile里指定这些built-in.o的路径,将它们连接在一起。
内核makefile.txt中将makefile分为 5部分,Kernel Makefile、ARCH Makefile、KBuild Makefile、.config文件以及scripts/Makefile.*
Kernel Makefile位于Linux内核源代码的顶层目录,也叫Top Makefile 。它主要用于指定编译Linux Kernel 目标文件(vmlinux)和模块(module)路径。它根据.config文件决定了内核根目录下那些文件、子目录被编译进内核。对于内核或驱动开发人员来说,这个文件几乎不用任何修改。
ARCH Makefile位于ARCH/$(ARCH)/Makefile,是系统对应平台的Makefile 。Kernel Top Makefile 会包含这个文件来指定平台相关信息。ARCH Makefile同样根据.config文件,决定了ARCH/$(ARCH) 目录下 那些文件、子目录被编译进内核 只有平台开发人员会关心这个文件。
从Linux内核2.6开始,Linux内核的编译采用Kbuild系统 ,这同过去的编译系统有很大的不同,Kbuild 系统使用Kbuild Makefile 来编译内核或模块。当Kernel Makefile 被解析完成后,Kbuild 会读取相关的Kbuild Makefile 进行内核或模块的编译。Kbuild Makefile 有特定的语法指定哪些编译进内核中、哪些编译为模块、及对应的源文件是什么等。内核及驱动开发人员需要编写这个Kbuild 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
来自配置过程,生成 auto.conf 以及 autoconf.h,被顶层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文件里。
如果内核模块是通过几个源文件编译而成的,您可以使用和上面同样的方法指定您想要编译的模块.然而此时Kbuild需要知道编译模块时是基于那些目标文件的,因此您需要设置一个$(
例如:#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