KBuild MakeFile介绍

<p>从Linux内核2.6开始,<span style="color: #0000ff;">Linux内核的编译采用Kbuild系统</span>
,这同过去的编译系统有很大的不同,
尤其对于Linux内核模块的编译。<span style="color: #0000ff;">在新的系统下,Linux编译系统会两次扫描Linux的Makefile:首先编译系统会读取Linux内核顶层的
Makefile,然后根据读到的内容第二次读取Kbuild的Makefile来编译Linux内核。</span>
<br><strong>Linux</strong>
<strong>内核Makefile分类</strong>
<br>
·    <strong><span style="color: #ff0000;">Kernel Makefile</span>
</strong>
<br>
Kernel Makefile位于Linux内核源代码的顶层目录,也叫 Top
Makefile。它主要用于指定编译Linux Kernel目标文件(vmlinux)和模块(module)。这编译内核或模块是,这个文件会被首先
读取,并根据读到的内容配置编译环境变量。对于内核或驱动开发人员来说,这个文件几乎不用任何修改。<br>
·   <strong><span style="color: #ff0000;">Kbuild Makefile</span>
</strong>
<br>
Kbuild系统使用Kbuild Makefile来编译内核或模块。当Kernel
Makefile被解析完成后,Kbuild会读取相关的Kbuild Makefile进行内核或模块的编译。Kbuild
Makefile有特定的语法指定哪些编译进内核中、哪些编译为模块、及对应的源文件是什么等。内核及驱动开发人员需要编写这个Kbuild
Makefile文件。<br>
·   <strong><span style="color: #ff0000;">ARCH Makefile</span>
</strong>
<br>
ARCH Makefile位于ARCH/$(ARCH)/Makefile,是系统对应平台的Makefile。Kernel Top
Makefile会包含这个文件来指定平台相关信息。只有平台开发人员会关心这个文件。<br><strong>Kbuild Makefile</strong>
<br>
  Kbuild Makefile的文件名不一定是<strong>Makefile</strong>
,尽管推荐使用Makefile这个名字。
大多的Kbuild文件的名字都是Makefile。为了与其他Makefile文件相区别,你也可以指定Kbuild Makefile的名字为<strong>Kbuild</strong>

而且如果“Makefile”和“Kbuild”文件同时存在,则Kbuild系统会使用“Kbuild”文件。<br>
·   <strong>目标定义 </strong>
<br>
Kbuild Makefile的一个最主要功能就是指定编译什么,这个功能是通过下面两个对象指定的obj-?和xxx-objs:<br>
·   <strong>obj-?</strong>
<br>
obj-?指定编译什么,怎么编译?其中的“?”可能是“y”或“m”,“y”指定把对象编译进内核中,“m”指定把对象编译为模块。语法如下;<br>
  obj-? = $(target).o<br>
target为编译对象的名字。如果没有指定xxx-objs,这编译这个对象需要的源文件就是$(target).c或$(target).s。如果指
定了$(target)-objs,则编译这个对象需要的源文件由$(target)-objs指定,并且不能有$(target).c
或$(target).s文件。<br>
·   <strong>xxx-objs </strong>
<br>
xxx-objs指定了编译对象需要的文件,一般只有在源文件是多个时才需要它。<br>
只要包含了这两行,Kbuild Makefile就应该可以工作了。<br>
·   <strong>嵌套编译 </strong>
<br>
有时一个对象可能嵌入到另一个对象的目录下,那个如何编译子目录下的对象呢?其实很简单,只要指定obj_?的对象为子目录的名字就可以了:<br>
obj-? = $(sub_target)/<br>
其中“?”可以是“y”或“m”,$(sub_target)是子目录名字。<br>
·   <strong>编译器选项 </strong>
<br>
尽管在大多数情况下不需要指定编译器选项,有时我们还是需要指定一些编译选项的。<br>
·   <strong>ccflags-y, asflags-y and ldflags-y </strong>
<br>
这些编译选项用于指定cc、as和ld的编译选项<br><strong>编译外部模块</strong>
<br>
有时候我们需要在内核源代码数的外面编译内核模块,编译的基本命令是:<br>
  make -C $(KERNEL_DIR)
M=`pwd` modules<br>
我们可以把这个命令集成到Makefile里,这样我们就可以只输入“make”命令就可以了。回想上一章的那个Makefile,它把Normal
Makefile 和KbuildMakefile集成到一个文件中了。为了区别Kbuild Makefile 和Normal
Makefile,这样我们改写Makefile为如下形式,并且添加Kbuild Makefile - “Kbuild”。<br>
##Makefile<br>
ifneq ($(KERNELRELEASE),)<br>
include "Kbuild"<br>
else<br>
KERNEL_DIR = /lib/modules/`uname -r`/build<br>
MODULEDIR := $(shell pwd)<br>
.PHONY: modules<br>
default: modules<br>
modules:<br>
  make -C $(KERNEL_DIR)M=$(MODULEDIR) modules<br>
clean distclean:<br>
  rm -f *.o *.mod.c .*.*.cmd *.ko<br>
  rm -rf .tmp_versions<br>
endif<br><br>
## Kbuild<br>
MODULE_NAME = helloworld<br>
$(MODULE_NAME)-objs := hello.o<br>
obj-m := $(MODULE_NAME).o<br>
一般不需要在Makefile里包含如下代码,这样写完全是为了兼容老版本的Kbuild系统。KERNELRELEASE变量在Kernel
Makefile里定义的,因此只有在第二次由Kbuild读取这个Makefile文件时才会解析到Kbuild的内容。 <br>
ifneq ($(KERNELRELEASE),)<br>
include "Kbuild"<br>
else<br>
...<br>
endif<br><strong>外部头文件</strong>
<br>
有时需要连接内核源代码外部的系统头文件,但Kbuild系统默认的系统头文件都在内核源代码内部,如何使用外部的头文件呢?这个可以借助于Kbuild
系统的特殊规则:<br>
·   <strong>EXTRA_CFLAGS </strong>
<br>
EXTRA_CFLAGS可以给Kbuild系统添加外部系统头文件,<br>
  EXTRA_CFLAGS += $(ext_include_path)<br>
一般外部头文件可能位于外部模块源文件的目录内,如何指定呢?这可以借助$(src)或$(obj)<br>
·   <strong>$(src)/$(obj) </strong>
<br>
$(src)是一个相对路径,它就是Makefile/Kbuild文件所在的路径。同样$(obj)就是编译目标保存的路径,默认就是源代码所在路径。<br>
因此,我们修改Kbuild文件添加 EXTRA_CFLAGS 来包含外部头文件尽管在这个驱动里没有引用外部系统头文件:<br>
## Kbuild<br>
MODULE_NAME = helloworld<br>
$(MODULE_NAME)-objs := hello.o<br>
EXTRA_CFLAGS := -I$(src)/include<br>
obj-m := $(MODULE_NAME).o<br><br>
//=========================================<br>
Linux Kernel Makefiles<br><br>
This document describes the Linux kernel Makefiles.<br><br>
=== Table of Contents<br><br>
=== 1 Overview<br>
=== 2 Who does what<br>
=== 3 The kbuild files<br>
  --- 3.1 Goal definitions<br>
  --- 3.2 Built-in object goals - obj-y<br>
  --- 3.3 Loadable module goals - obj-m<br>
  --- 3.4 Objects which export symbols<br>
  --- 3.5 Library file goals - lib-y<br>
  --- 3.6 Descending down in directories<br>
  --- 3.7 Compilation flags<br>
  --- 3.8 Command line dependency<br>
  --- 3.9 Dependency tracking<br>
  --- 3.10 Special Rules<br>
  --- 3.11 $(CC) support functions<br>
  --- 3.12 $(LD) support functions<br><br>
=== 4 Host Program support<br>
  --- 4.1 Simple Host Program<br>
  --- 4.2 Composite Host Programs<br>
  --- 4.3 Defining shared libraries<br>
  --- 4.4 Using C++ for host programs<br>
  --- 4.5 Controlling compiler options for host programs<br>
  --- 4.6 When host programs are actually built<br>
  --- 4.7 Using hostprogs-$(CONFIG_FOO)<br><br>
=== 5 Kbuild clean infrastructure<br><br>
=== 6 Architecture Makefiles<br>
  --- 6.1 Set variables to tweak the build to the architecture<br>
  --- 6.2 Add prerequisites to archprepare:<br>
  --- 6.3 List directories to visit when descending<br>
  --- 6.4 Architecture-specific boot images<br>
  --- 6.5 Building non-kbuild targets<br>
  --- 6.6 Commands useful for building a boot image<br>
  --- 6.7 Custom kbuild commands<br>
  --- 6.8 Preprocessing linker scripts<br><br>
=== 7 Kbuild syntax for exported headers<br>
  --- 7.1 header-y<br>
  --- 7.2 objhdr-y<br>
  --- 7.3 destination-y<br>
  --- 7.4 unifdef-y (deprecated)<br><br>
=== 8 Kbuild Variables<br>
=== 9 Makefile language<br>
=== 10 Credits<br>
=== 11 TODO<br><br>
·   <span style="color: #0080ff;">Goal definitions</span>
<br><strong>Example:</strong>
<br>
obj-y += foo.o<br>
告诉kbuild,在文件夹中又一个叫做foo.o的object。foo.o将会被从foo.c或者foo.S被构建。<br><br>
如果foo.o被构建成一个<strong>模块</strong>
,则将使用变量<strong>obj-m</strong>
。<strong>Example</strong>
:<br>
obj-$(CONFIG_FOO) += foo.o<br>
$(CONFIG_FOO)要么是y(built-in)要么是m(module)。如果CONFIG_FOO既不是y也不是m,那么文件将不会被编译也
不会被连接。<br><br>
·   <span style="color: #0080ff;">Built-in object goals - obj-y</span>
<br>
kbuild Makefiles在<strong>$(obj-y)</strong>
列表中为vmlinux指明object文件。这个列表依靠内核
的配置。<br>
在<strong>$(obj-y)</strong>
中的文件的顺序是非常重要的。列表中允许两个相同的文件:<strong>第一个</strong>

体将被连接到built-in.o,后面的实体将会<strong>被忽略。</strong>
<br>
连接的顺序也很重要,因为在boot过程中某些函数(module_init()/_initcall)将会<strong>按顺序出现。</strong>

此,如果改变了连接顺序,将会改变你的SCSI控制器的检测顺序,你的磁盘也同时被重新编号了。<br><strong> Example:</strong>
<br>
#drivers/isdn/i4l/Makefile<br>
# Makefile for the kernel ISDN subsystem and
device drivers.<br>
# Each configuration option enables a list of files.<br>
obj-$(CONFIG_ISDN)     += isdn.o<br>
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o<br><br>
·   <span style="color: #0080ff;">Loadable module goals - obj-m</span>
<br><strong>$(obj-m)</strong>
指明object文件作为可装载的内核模块被构建。一个模块可能从一个或者多个源文件被构建。
kbuild maefile只是简单的将源文件加到%(obj-m)<br><strong> Example:</strong>
<strong><br></strong>
#drivers/isdn/i4l/Makefile<br>
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o<br>
注意这里$(CONFIG_ISDN_PPP_BSDCOMP)是m.<br>
Note: In this example $(CONFIG_ISDN_PPP_BSDCOMP) evaluates to 'm'。<br>
如果一个内核模块从多个源文件构建,KBuild就必须要知道你想从哪些部分构建模块。因此,你不得不设置<strong>$(-objs</strong>
)
变量来告诉KBuild。<br><strong> Example:</strong>
<br>
#drivers/isdn/i4l/Makefile<br>
obj-$(CONFIG_ISDN) += isdn.o<br>
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o<br>
在这个例子中,模块名是isdn.o,Kbuild将会编译列在<strong>$(isdn-objs)</strong>
<strong>的</strong>
object
文件,然后在这些文件的列表中调用"$(LD) -r"来产生isdn.o。<br>
Kbuild使用<strong>后缀-objs,-y</strong>
来识别混合的object文件。这允许Makefiles使用变量<strong>CONFIG_sambol</strong>

决定一个object是否是混合object的的一部分。<br><strong> Example:</strong>
<br>
#fs/ext2/Makefile<br>
   obj-$(CONFIG_EXT2_FS)  += ext2.o<br>
ext2-y       := balloc.o bitmap.o<br>
   ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o<br><br>
在这个例子中,如果$(CONFIG_EXT2_FS_XATTR)是y,则xattr.o只是混合object文件ext2.o的一部分。<br>

注意,当你构造一个objects到内核中时,上面的语法当然也能够工作。因此,如果你让CONFIG_EXT2=Y,KBuild将会为你构建一个独立
的ext2.o文件,并且连接到built-in.o。<br><br>
·   <span style="color: #0080ff;"></span>
<strong style="color: #0080ff;">Library file goals - lib-y</strong>
<br>
用<strong>obj-*</strong>
连接的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文件。<br><strong>Example:</strong>
<strong><br></strong>
#arch/i386/lib/Makefile<br>
lib-y  := checksum.o delay.o<br>
这里讲会创建一个基于checksum.o和delay.o的库文件。对于kbuild,识别一个lib.a正在被构建,这个文件夹应该被列在<strong>libs-y</strong>
中。<strong>lib-y</strong>

使用方法通常被限制在lib/和arc/*/lib中。<br><br>
·   <strong style="color: #0080ff;">Descending down in directories</strong>
<br>
一个Makefile只负责在他自己的文件夹中构建objects。
在子文件夹中的文件应该由子文件夹中的Makefiles来照顾。如果你知道他们,build系统将会自动递归地用在子文件夹中的make。<br>
在这种情况下<strong>obj-y</strong>
和<strong>obj-m</strong>
就被使用了。ext2存在于不同的文件夹
中,Makefile出现在fs/,则告诉kbuild从后面的参数下来。<br><strong>Example:</strong>
<br>
#fs/Makefile<br>
obj-$(CONFIG_EXT2_FS) += ext2/<br>

如果CONFIG_EXT2_FS被设置成y(built-in)或者m(modular),相应的obj-变量将会被设置,并且kbuild将会从
ext2文件夹继承下来。Kbuild只会使用这些信息来决定它需要访问这些文件夹,而在子文件夹中的Makefile来指
明哪些是modules哪些是built-in。<br>
当赋值文件夹名字的时候,使用CONFIG_variable是很好的选择。这允许kbuild完全的跳过文件夹,而不管CONFIG_option是否
是y或者m。<br><br>
·   <strong style="color: #0080ff;">Compilation flags</strong>
<br>
  EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS。<br>
所有的EXTRA_ variables只应用在kbuild中,他们被赋值的地方。EXTRA_variables应用在kbuild
makefile中所有的可执行的命令。<strong>$(EXTRA_CFLAGS)</strong>
指明用$(CC)编译C文件的时候的选项。<br><strong>Example:</strong>
<strong><br></strong>
# drivers/sound/emu10k1/Makefile<br>
EXTRA_CFLAGS += -I$(obj)<br>
ifdef DEBUG<br>
  EXTRA_CFLAGS += -DEMU10K1_DEBUG<br>
endif<br>
这里的变量是必须的,因为顶层的Makefile拥有变量<strong>$(CFLAGS)</strong>
并且用它来作为整个树的编译标志当编译汇编源文件的时候<strong>$(EXTRA_AFLAGS)</strong>

和每个文件夹的选项是相似的。<br><strong> Example:</strong>
<br>
#arch/x86_64/kernel/Makefile<br>
EXTRA_AFLAGS := -traditional<br><strong>$(EXTRA_LDFLAGS)</strong>
和<strong>$(EXTRA_ARFLAGS)</strong>

对于每个文件夹的$(LD)和$(AR)选项是类似的。<br><strong> Example:</strong>
<br>
#arch/m68k/fpsp040/Makefile<br>
EXTRA_LDFLAGS := -x<br>
CFLAGS_$@, AFLAGS_$@<br><strong>CFLAGS_$@</strong>
和<strong>AFLAGS_$@</strong>
只应用到当前kbuild
makefile的命令。<br><strong> $(CFLAGS_$@)</strong>
为每个文件的$(CC)指明选项。<strong>$@</strong>
<strong><br></strong>
部分有一个字面上的值,指明它是为那个文件。<br><strong> Example:</strong>
<br>
# drivers/scsi/Makefile<br>
CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF<br>
CFLAGS_gdth.o  = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ /<br>
   -DGDTH_STATISTICS<br>
CFLAGS_seagate.o = -DARBITRATE -DPARITY -DSEAGATE_USE_ASM<br>
These three lines specify compilation flags for aha152x.o,<br>
gdth.o, and seagate.o<br>
$(AFLAGS_$@) is a similar feature for source files in assembly<br>
languages.<br>
Example:<br>
# arch/arm/kernel/Makefile<br>
AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR) -traditional<br>
AFLAGS_head-armo.o := -DTEXTADDR=$(TEXTADDR) -traditional<br><br><br><strong>本文来自ChinaUnix博客,如果查看原文请点:</strong>
<a href="http://blog.chinaunix.net/u3/94284/showart_1896000.html" target="_blank">http://blog.chinaunix.net/u3/94284/showart_1896000.html</a>
</p>

你可能感兴趣的:(makefile)