实际文档位置:Documentation/kbuild/makefiles.txt,此为翻译稿。
*******************************************************************************
Linux内核的Makefile
=== 目录
=== 1 概述
=== 2 用户与做用
=== 3 Kbuild文件
--- 3.1 目标定义
--- 3.2 编译进内核 - obj-y
--- 3.3 编译可装载模块 - obj-m
--- 3.4 输出的符号
--- 3.5 目标库文件 - lib-y
--- 3.6 递归躺下访问目录
--- 3.7 编辑标志
--- 3.8 命令行的依赖关系(原文中没有写:-))
--- 3.9 跟踪依赖
--- 3.10 特殊规则
--- 3.11 $(CC) 支持的函数
=== 4 本机程序支持
--- 4.1 简单的本机程序
--- 4.2 复合的本机程序
--- 4.3 定义共享库
--- 4.4 使用用C++编写的本机程序
--- 4.5 控制本机程序的编译选项
--- 4.6 编译主机程序时
--- 4.7 使用 hostprogs-$(CONFIG_FOO)
=== 5 Kbuild清理
=== 6 架构Makefile
--- 6.1 调整针对某一具体架构生成的镜像
--- 6.2 将所需文件加到 archprepare 中
--- 6.3 递归下向时要访问的目录列表
--- 6.4 具体架构的启动镜像
--- 6.5 构造非Kbuild目标
--- 6.6 构建启动镜像的命令
--- 6.7 Kbuild自定义命令
--- 6.8 联接器预处理脚本
=== 7 Kbuild 变量
=== 8 Makefile语言
=== 9 关于做者
=== 10 TODO
=== 1 概述
Linux内核的Makefile分为5个部分:
Makefile 顶层Makefile
.config 内核配置文件
arch/$(ARCH)/Makefile 具体架构的Makefile
scripts/Makefile.* 通用的规则等。面向全部的Kbuild Makefiles。
kbuild Makefiles 内核源代码中大约有500个这样的文件
顶层Makefile阅读的.config文件,而该文件是由内核配置程序生成的。
顶层Makefile负责制做:vmlinux(内核文件)与模块(任何模块文件)。制做的过程主要是
经过递归向下访问子目录的形式完成。并根据内核配置文件肯定访问哪些子目录。顶层
Makefile要原封不动的包含一具体架构的Makefile,其名字相似于 arch/$(ARCH)/
Makefile。该架构Makefile向顶层Makefile提供其架构的特别信息。
每个子目录都有一个Kbuild Makefile文件,用来执行从其上层目录传递下来的命令。
Kbuild Makefile从.config文件中提取信息,生成Kbuild完成内核编译所需的文件列表。
scripts/Makefile.*包含了全部的定义、规则等信息。这些文件被用来编译基于kbuild
Makefile的内核。(**有点不通**)
=== 2 用户与做用
能够将人们与内核Makefile的关系分红4类。
*使用者* 编译内核的人。他们只是键入"make menuconfig"或"make"这样的命令。通常
状况下是不会读或编辑任何内核Makefile(或者任何的源文件)。
*普通开发人员* 这是一群工做在内核某一功能上的人,好比:驱动开发,文件系统或
网络协议。他们所须要维护的只是他们所工做的子系统的Kbuild Makefile。为了提升
工做的效率,他们也须要对内核Makefile有一个全面的认识,而且要熟悉Kbuild的接口
。
*架构开发人员* 这是一些工做在具体架构,好比sparc 或者ia64,上面的人。架构开
发者须要在熟悉kbuild Makefile的同时,也要熟悉他所工做架构的Makefile。
*Kbuild开发者* 维护Kbuild系统的人。他们须要知晓内核Makefile的方方面面。
该文件是为普通开发人员与架构开发人员所写。
=== 3 Kbuild文件
大部份内核中的Makefile都是使用Kbuild组织结构的Kbuild Makefile。这章介绍了
Kbuild Makefile的语法。
Kbuild文件倾向于"Makefile"这个名字,"Kbuild"也是能够用的。但若是"Makefile"
"Kbuild"同时出现的话,使用的将会是"Kbuild"文件。
3.1节 目标定义是一个快速介绍,之后的几章会提供更详细的内容以及实例。
--- 3.1 目标定义
目标定义是Kbuild Makefile的主要部分,也是核心部分。主要是定义了要编
译的文件,全部的选项,以及到哪些子目录去执行递归操做。
最简单的Kbuild makefile 只包含一行:
例子:
obj-y += foo.o
该例子告诉Kbuild在这目录里,有一个名为foo.o的目标文件。foo.o将从foo.c
或foo.S文件编译获得。
若是foo.o要编译成一模块,那就要用obj-m了。所采用的形式以下:
例子:
obj-$(CONFIG_FOO) += foo.o
$(CONFIG_FOO)能够为y(编译进内核) 或m(编译成模块)。若是CONFIG_FOO不是y
和m,那么该文件就不会被编译联接了。
--- 3.2 编译进内核 - obj-y
Kbuild Makefile 规定全部编译进内核的目标文件都存在$(obj-y)列表中。而
这些列表依赖内核的配置。
Kbuild编译全部的$(obj-y)文件。而后,调用"$(LD) -r"将它们合并到一个
build-in.o文件中。稍后,该build-in.o会被其父Makefile联接进vmlinux中。
$(obj-y)中的文件是有顺序的。列表中有重复项是能够的:当第一个文件被联
接到built-in.o中后,其他文件就被忽略了。
联接也是有顺序的,那是由于有些函数(module_init()/__initcall)将会在启
动时按照他们出现的顺序进行调用。因此,记住改变联接的顺序可能改变你
SCSI控制器的检测顺序,从而致使你的硬盘数据损害。
例子:
#drivers/isdn/i4l/Makefile
# Makefile for the kernel ISDN subsystem and device drivers.
# Each configuration option enables a list of files.
obj-$(CONFIG_ISDN) += isdn.o
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
--- 3.3 编译可装载模块 - obj-m
$(obj-m) 列举出了哪些文件要编译成可装载模块。
一个模块能够由一个文件或多个文件编译而成。若是是一个源文件,Kbuild
Makefile只需简单的将其加到$(obj-m)中去就能够了。
例子:
#drivers/isdn/i4l/Makefile
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
注意:此例中 $(CONFIG_ISDN_PPP_BSDCOMP) 的值为'm'
若是内核模块是由多个源文件编译而成,那你就要采用上面那个例子同样的
方法去声明你所要编译的模块。
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
在这个例子中,模块名将是isdn.o,Kbuild将编译在$(isdn-objs)中列出的
全部文件,而后使用"$(LD) -r"生成isdn.o。
Kbuild可以识别用于组成目标文件的后缀-objs和后缀-y。这就让Kbuild
Makefile能够经过使用 CONFIG_ 符号来判断该对象是不是用来组合对象的。
例子:
#fs/ext2/Makefile
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
在这个例子中,若是 $(CONFIG_EXT2_FS_XATTR) 是 'y',xattr.o将是复合
对象 ext2.o的一部分。
注意:固然,当你要将其编译进内核时,上面的语法一样适用。因此,若是
你的 CONFIG_EXT2_FS=y,那Kbuild会按你所指望的那样,生成 ext2.o文件
,而后将其联接到 built-in.o中。
--- 3.4 输出的符号
在Makefile中,没有对模块输出的符号有特殊要求。
--- 3.5 目标库文件 - lib-y
在 obj-* 中所列文件是用来编译模块或者是联接到特定目录中的 built-in.o
。一样,也能够列出一些将被包含在lib.a库中的文件。
在 lib-y 中所列出的文件用来组成该目录下的一个库文件。
在 obj-y 与 lib-y 中同时列出的文件,由于都是能够访问的,因此该文件是
不会被包含在库文件中的。
一样的状况, lib-m 中的文件就要包含在 lib.a 库文件中。
注意,一个Kbuild makefile能够同时列出要编译进内核的文件与要编译成库
的文件。因此,在一个目录里能够同时存在 built-in.o 与 lib.a 两个文件。
例子:
#arch/i386/lib/Makefile
lib-y := chechsum.o delay.o
这将由 checksum.o 和delay.o 两个文件建立一个库文件 lib.a。为了让
Kbuild 真正认识到这里要有一个库文件 lib.a 要建立,其所在的目录要加
到 libs-y 列表中。
还可参考"6.3 递归下向时要访问的目录列表"
lib-y 使用通常限制在 lib/ 和 arch/*/lib 中。
--- 3.6 递归向下访问目录
一个Makefile只对编译所在目录的对象负责。在子目录中的文件的编译要由
其所在的子目录的Makefile来管理。只要你让Kbuild知道它应该递归操做,
那么该系统就会在其子目录中自动的调用 make 递归操做。
这就是 obj-y 和 obj-m 的做用。
ext2 被放的一个单独的目录下,在fs目录下的Makefile会告诉Kbuild使用
下面的赋值进行向下递归操做。
例子:
#fs/Makefile
obj-$(CONFIG_EXT2_FS) += ext2/
若是 CONFIG_EXT2_FS 被设置为 'y'(编译进内核)或是'm'(编译成模块),相
应的 obj- 变量就会被设置,而且Kbuild就会递归向下访问 ext2 目录。
Kbuild只是用这些信息来决定它是否须要访问该目录,而具体怎么编译由该目
录中的Makefile来决定。
将 CONFIG_ 变量设置成目录名是一个好的编程习惯。这让Kbuild在彻底忽略那
些相应的 CONFIG_ 值不是'y'和'm'的目录。
--- 3.7 编辑标志
EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS
全部的 EXTRA_ 变量只在所定义的Kbuild Makefile中起做用。EXTRA_ 变量可
以在Kbuild Makefile中全部命令中使用。
$(EXTRA_CFLAGS) 是用 $(CC) 编译C源文件时的选项。
例子:
# drivers/sound/emu10kl/Makefile
EXTRA_CFLAGS += -I$(obj)
ifdef DEBUG
EXTRA_CFLAGS += -DEMU10KL_DEBUG
endif
该变量是必须的,由于顶层Makefile拥有变量 $(CFLAGS) 并用来做为整个源
代码树的编译选项。
$(EXTRA_AFLAGS) 也是一个针对每一个目录的选项,只不过它是用来编译汇编
源代码的。
例子:
#arch/x86_64/kernel/Makefile
EXTRA_AFLAGS := -traditional
$(EXTRA_LDFLAGS) 和 $(EXTRA_ARFLAGS)分别与 $(LD)和 $(AR)相似,只不
过,他们是针对每一个目录的。
例子:
#arch/m68k/fpsp040/Makefile
EXTRA_LDFLAGS := -x
CFLAGS_$@, AFLSGA_$@
CFLAGS_$@ 和 AFLAGS_$@ 只能在当前Kbuild Makefile中的命令中使用。
$(CFLAGS_$@) 是 $(CC) 针对每一个文件的选项。$@ 代表了具体操做的文件。
例子:
# drivers/scsi/Makefile
CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF
CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ /
-DGDTH_STATISTICS
CFLAGS_seagate.o = -DARBITRATE -DPARITY -DSEAGATE_USE_ASM
以上三行分别设置了aha152x.o,gdth.o 和 seagate.o的编辑选项。
$(AFLAGS_$@) 也相似,只不是是针对汇编语言的。
例子:
# arch/arm/kernel/Makefile
AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR) -traditional
AFLAGS_head-armo.o := -DTEXTADDR=$(TEXTADDR) -traditional
--- 3.9 跟踪依赖
Kbuild 跟踪在如下方面依赖:
1) 全部要参与编译的文件(全部的.c 和.h文件)
2) 在参与编译文件中所要使用的 CONFIG_ 选项
3) 用于编译目标的命令行
所以,若是你改变了 $(CC) 的选项,全部受影响的文件都要从新编译。
--- 3.10 特殊规则
特殊规则就是那Kbuild架构不能提供所要求的支持时,所使用的规则。一个
典型的例子就是在构建过程当中生成的头文件。
另外一个例子就是那些须要采用特殊规则来准备启动镜像。
特殊规则的写法与普通Make规则同样。
Kbuild并不在Makefile所在的目录执行,因此全部的特殊规则都要提供参与
编译的文件和目标文件的相对路径。
在定义特殊规则时,要使用如下两个变量:
$(src)
$(src) 代表Makefile所在目录的相对路径。常常在定位源代码树中的文件时
,使用该变量。
$(obj)
$(obj) 代表目标文件所要存储目录的相对路径。常常在定位所生成的文件时
,使用该变量。
例子:
#drivers/scsi/Makefile
$(obj)/53c8xx_d.h: $(src)/53c7,8xx.scr $(src)/script_asm.pl
$(CPP) -DCHIP=810 - < $< | ... $(src)/script_asm.pl
这就是一个特殊规则,遵照着make所要求的普通语法。
目标文件依赖于两个源文件。用$(obj)来定位目标文件,用$(src)来定位源文
件(由于它们不是咱们生成的文件)。
--- 3.11 $(CC) 支持的函数
内核可能由多个不一样版本的$(CC)编译,而每一个版本都支持一不一样的功能集与
选项集。Kbuild提供了检查 $(CC) 可用选项的基本功能。$(CC)通常状况下是
gcc编译器,但也可使用其它编译器来代替gcc。
as-option
as-option,当编译汇编文件(*.S)时,用来检查 $(CC) 是否支持特定选项。如
果第一个选项不支持的话,可选的第二个选项能够用来指定。
例子:
#arch/sh/Makefile
cflags-y += $(call as-option,-Wa$(comma)-isa=$(isa-y),)
在上面的例子里,若是 $(CC) 支持选项 -Wa$(comma)-isa=$(isa-y),
cflags-y就会被赋予该值。
第二个参数是可选的,当第一个参数不支持时,就会使用该值。
ld-option
ld-option,当联接目标文件时,用来检查 $(CC) 是否支持特定选项。若是第
一个选项不支持的话,可选的第二个选项能够用来指定。
例子:
#arch/i386/kernel/Makefile
vsyscall-flags += $(call ld-option, -Wl$(comma)--hash-style=sysv)
在上面的例子中,若是 $(CC)支持选项 -Wl$(comma)--hash-style=sysv,
ld-option就会被赋予该值。
第二个参数是可选的,当第一个参数不支持时,就会使用该值。
cc-option
cc-option,用来检查 $(CC) 是否支持特定选项,而且不支持使用可选的第二
项。
例子:
#arch/i386/Makefile
cflags-y += $(call cc-option,-march=pentium-mmx,-march=i586)
在上面的例子中,若是 $(CC)支持选项 -march=pentium-mmx,cc-option就
会被赋予该值,不然就赋 -march-i586。
cc-option的第二个参数是可选的。若是忽略的话,当第一个选项不支持时,
cflags-y 不会被赋值。
cc-option-yn
cc-option-yn,用来检查 gcc 是否支持特定选项,返回'y'支持,不然为'n'。
例子:
#arch/ppc/Makefile
biarch := $(call cc-option-yn, -m32)
aflags-$(biarch) += -a32
cflags-$(biarch) += -m32
在上面的例子里,当 $(CC) 支持 -m32选项时,$(biarch)设置为y。当
$(biarch) 为y时,扩展的 $(aflags-y) 和 $(cflags-y)变量就会被赋值为
-a32 和 -m32。
cc-option-align
gcc版本大于3.0时,改变了函数,循环等用来声明内存对齐的选项。当用到
对齐选项时,$(cc-option-align) 用来选择正确的前缀:
gcc < 3.00
cc-option-align = -malign
gcc >= 3.00
cc-option-align = -falign
例子:
CFLAGS += $(cc-option-align)-functions=4
在上面的例子中,选项 -falign-funcions=4 被用在gcc >= 3.00的时候。对
于小于3.00时, 使用 -malign-funcions=4 。
cc-version
cc-version以数学形式返回 $(CC) 编译器的版本号。
其格式是:<major><minor>,两者都是数学。好比,gcc 3.41 会返回 0341。
当某版本的 $(CC) 在某方面有缺陷时,cc-version就会颇有用。好比,选项
-mregparm=3 虽然会被gcc接受,但其实现是有问题的。
例子:
#arch/i386/Makefile
cflags-y += $(shell /
if [ $(call cc-version) -ge 0300 ] ; then /
echo "-meregparm=3"; fi ;)
在上面的例子中,-mregparm=3只会在gcc的版本号大于等于3.0的时候使用。
cc-ifversion
cc-ifversion测试 $(CC) 的版本号,若是版本表达式为真,就赋值为最后的
参数。
例子:
#fs/reiserfs/Makefile
EXTRA_CFLAGS := $(call cc-ifversion, -lt, 0402, -O1)
在这个例子中,若是 $(CC) 的版本小于4.2,EXTRA_CFLAGS就被赋值 -O1。
cc-ifversion 可以使用全部的shell 操做符:-eq,-ne,-lt,-le,-gt,和-ge。
第三个参数能够像上面例子同样是个文本,但也能够是个扩展的变量或宏。
/*这段翻译的很差*/
=== 4 本机程序支持
Kbuild 支持编译那些将在编译阶段使用的可执行文件。
为了使用该可执行文件,要将编译分红二个阶段。
第一阶段是告诉Kbuild存在哪些可执行文件。这是经过变量 hostprogs-y来完成的。
第二阶段是添加一个对可执行文件的显性依赖。有两种方法:增长依赖关系到一个规则
中,或是利用变量 $(always)。
如下是详细叙述.
--- 4.1 简单的本机程序
在编译内核时,有时会须要编译并运行一个程序。
下面这行就告诉了kbuild,程序bin2hex应该在本机上编译。
例子:
hostprogs-y := bin2hex
在上面的例子中,Kbuild假设bin2hex是由一个与其在同一目录下,名为
bin2hex.c 的C语言源文件编译而成的。
--- 4.2 复合的本机程序
本机程序能够由多个文件编译而成。
所使用的语法与内核的相应语法很类似。
$(<executeable>-objs) 列出了联接成最后的可执行文件所需的全部目标文件。
例子:
#scripts/lxdialog/Makefile
hostprogs-y := lxdialog
lxdialog-objs := checklist.o lxdialog.o
扩展名为.o的文件是从相应的.c文件编译而来的。在上面的例子中,
checklist.c 编译成了checklist.o,lxdialog.c编译成了lxdialog.o。
最后,两个.o文件联接成了一可执行文件,lxdialog。
注意:语法 <executable>-y不是只能用来生成本机程序。
--- 4.3 定义共享库
扩展名为so的文件称为共享库,被编译成位置无关对象。
Kbuild也支持共享库,但共享库的使用颇有限。
在下面的例子中,libconfig.so共享库用来联接到可执行文件 conf中。
例子:
#scripts/kconfig/Makefile
hostprogs-y := conf
conf-objs := conf.o libkconfig.so
libkcofig-objs := expr.o type.o
共享库文件常常要求一个相应的 -objs,在上面的例子中,共享库libkconfig
是由 expr.o 和 type.o两个文件组成的。
expr.o 和 type.o 将被编译成位置无关码,而后联接成共享库文件
libkconfig.so。C++并不支持共享库。
--- 4.4 使用用C++编写的本机程序
kbuild也支持用C++编写的本机程序。在此专门介绍是为了支持kconfig,而且
在通常状况下不推荐使用。
例子:
#scripts/kconfig/Makefile
hostprogs-y := qconf
qconf-cxxobjs := qconf.o
在上面的例子中,可执行文件是由C++文件 qconf.cc编译而成的,由
$(qconf-cxxobjs)来标识。
若是qconf是由.c和.cc一块儿编译的,那么就须要专门来标识这些文件了。
例子:
#scripts/kconfig/Makefile
hostprogs-y := qconf
qconf-cxxobjs := qconf.o
qconf-objs := check.o
--- 4.5 控制本机程序的编译选项
当编译本机程序时,有可能使用到特殊选项。程序常常是利用$(HOSTCC)编译
,其选项在 $(HOSTCFLAGS)变量中。
可经过使用变量 HOST_EXTRACFLAGS,影响全部在Makefile文件中要建立的
主机程序。
例子:
#scripts/lxdialog/Makefile
HOST_EXTRACFLAGS += -I/usr/include/ncurses
为一单个文件设置选项,可按形式进行:
例子:
#arch/ppc64/boot/Makefile
HOSTCFLAGS_pinggyback.o := -DKERNELBASE=$(KERNELBASE)
一样也能够给联接器声明一特殊选项。
例子:
#scripts/kconfig/Makefile
HOSTLOADLIBES_qconf := -L$(QTDIR)/lib
当联接qconf时,将会向联接器传递附加选项 "-L$(QTDIR)/lib"。
--- 4.6 编译主机程序时
Kbuild只在须要时编译主机程序。
有两种方法:
(1) 在一具体的规则中显性列出所须要的文件
例子:
#drivers/pci/Makefile
hostprogs-y := gen-devlist
$(obj)/devlist.h: $(src)/pci.ids $(obj)/gen-devlist
( cd $(obj); ./gen-devlist ) < $<
目标 $(obj)/devlist.h 是不会在 $(obj)/gen-devlist 更新以前编译的。注意
在该规则中全部有关主机程序的命令必须以$(obj)开头。
(2) 使用 $(always)
当Makefile要编译主机程序,但没有适合的规则时,使用 $(always)。
例子:
#scripts/lxdialog/Makefile
hostprogs-y := lxdialog
always := $(hostprogs-y)
这就是告诉Kbuild,即便没有在规则中声明,也要编译 lxdialog。
--- 4.7 使用 hostprogs-$(CONFIG_FOO)
一个典型的Kbuild模式以下:
例子:
#scripts/Makefile
hostprogs-$(CONFIG_KALLSYMS) += kallsyms
Kbuild 知道 'y' 是编译进内核,而 'm' 是编译成模块。因此,若是配置符号
是'm',Kbuild仍然会编译它。换句话说,Kbuild处理 hostprogs-m 与
hostprogs-y 的方式是彻底一致的。只是,若是不用 CONFIG,最好用
hostprogs-y。
=== 5 Kbuild清理(clean)
"make clean"删除几乎全部的在编译内核时生成的文件,包括了主机程序在内。
Kbuild 经过列表 $(hostprogs-y),$(hostprogs-m),$(always),$(extra-y) 和
$(targets) 知道所要编译的目标。这些目标文件都会被 "make clean" 删除。另外
,在"make clean"还会删除匹配 "*.[oas]","*.ko" 的文件,以及由 Kbuild生成
的辅助文件。
辅助文件由 Kbuild Makefile 中的 $(clean-files) 指明。
例子:
#drivers/pci/Makefile
clean-files := devlist.h classlist.h
当执行 "make clean" 时,"devlist.h classlist.h"这两个文件将被删除。若是不使用
绝对路径(路径以'/'开头)的话,Kbuild假设所要删除的文件与Makefile在同一个相对路
径上。
要删除一目录:
例子:
#scripts/package/Makefile
clean-dirs := $(objtree)/debian/
这就会删除目录 debian,包括其全部的子目录。若是不使用绝对路径(路径以'/'开头)的
话,Kbuild假设所要删除的目录与Makefile在同一个相对路径上。
通常状况下,Kbuild会根据 "obj-* := dir/" 递归访问其子目录,但有的时候,Kbuild
架构还不足以描述全部的状况时,还要显式的指明所要访问的子目录。
例子:
#arch/i386/boot/Makefile
subdir- := compressed/
上面的赋值命令告诉Kbuild,当执行"make clean"时,要递归访问目录 compressed/。
为了支持在最终编译完成启动镜像后的架构清理工做,还有一可选的目标 archclean:
例子:
#arch/i386/Makefile
archclean:
$(Q)$(MAKE) $(clean)=arch/i386/boot
当"make clean"执行时,make会递归访问并清理 arch/i386/boot。在 arch/i386/boot
中的Makefile能够用来提示make进行下一步的递归操做。
注意1:arch/$(ARCH)/Makefile 不能使用"subdir-",由于该Makefile被包含在顶层的
Makefile中,Kbuild是不会在此处进行操做的。
注意2:"make clean" 会访问在 core-y,libs-y,drivers-y 和 net-y 列出的全部目
录。
=== 6 架构Makefile
在递归访问目录以前,顶层Makefile要完成设置环境变量以及递归访问的准备工做。顶
层Makefile包含的公共部分,而 arch/$(ARCH)/Makefile 包含着针对某一特定架构的
配置信息。
因此,要在 arch/$(ARCH)/Makefile 中设置一部分变量,并定义一些目标。
Kbuild执行的几个步驟(大体):
1) 根据内核配置生成文件 .config
2) 将内核的版本号存储在 include/linux/version.h
3) 生成指向 include/asm-$(ARCH) 的符号连接
4) 更新全部编译所需的文件:
-附加的文件由 arch/$(ARCH)/Makefile 指定。
5) 递归向下访问全部在下列变量中列出的目录: init-* core* drivers-* net-*
libs-*,并编译生成目标文件。
-这些变量的值能够在 arch/$(ARCH)/Makefile 中扩充。
6) 联接全部的目标文件,在源代码树顶层目录中生成 vmlinux。最早联接是在 head-y中
列出的文件,该变量由 arch/$(ARCH)/Makefile 赋值。
7) 最后完成具体架构的特殊要求,并生成最终的启动镜像。
-包含生成启动指令
-准备 initrd 镜像或相似文件
--- 6.1 调整针对某一具体架构生成的镜像
LDFLAGS 通常是 $(LD) 选项
该选项在每次调用联接器时都会用到。
通常状况下,只用来指明模拟器。
例子:
#arch/s390/Makefile
LDFLAGS := -m elf_s390
注意:EXTRA_LDFLAGS 和 LDFLAGS_$@ 可用来进一步自定义选项。请看第七章。
LDFLAGS_MODULE 联接模块时的联接器的选项
LDFLAGS_MODULE 所设置的选项将在联接器在联接模块文件 .ko 时使用。
默认值为 "-r",指定输出文件是可重定位的。
LDFLAGS_vmlinux 联接vmlinux时的选项
LDFLAGS_vmlinux用来传递联接vmlinux时的联接器的选项。
LDFLAGS_vmlinux需 LDFLAGS_$@ 支持。
例子:
#arch/i386/Makefile
LDFLAGS_vmlinux := -e stext
OBJCOPYFLAGS objcopy 选项
当用 $(call if_changed,objcopy) 来转换(translate)一个.o文件时,该选项
就会被使用。
$(call if_changed,objcopy) 常常被用来为vmlinux生成原始的二进制代码。
例子:
#arch/s390/Makefile
OBJCOPYFLAGS := -O binary
#arch/s390/boot/Makefile
$(obj)/image: vmlinux FORCE
$(call if_changed,objcopy)
在此例中,二进制文件 $(obj)/image 是 vmlinux 的一个二进制版本。
$(call if_chagned,xxx)的用法稍后描述。
AFLAGS $(AS) 汇编编译器选项
默认值在顶层Makefile
扩充或修改在各具体架构的Makefile
例子:
#arch/sparc64/Makefile
AFLAGS += -m64 -mcpu=ultrasparc
CFLAGS $(CC) 编译器选项
默认值在顶层Makefile
扩充或修改在各具体架构的Makefile。
通常,CFLAGS要根据内核配置设置。
例子:
#arch/i386/Makefile
cflags-$(CONFIG_M386) += -march=i386
CFLAGS += $(cflags-y)
许多架构Makefile都经过调用所要使用的C编译器,动态的检查其所支持的选
项:
#arch/i386/Makefile
...
cflags-$(CONFIG_MPENTIUMII) += $(call cc-option,/
-march=pentium2,-march=i686)
...
# Disable unit-at-a-time mode ...
CFLAGS += $(call cc-option,-fno-unit-at-a-time)
...
第一个例子利用了一个配置选项,当其为'y'时,扩展。
CFLAGS_KERNEL :
#arch/i386/Makefile
...
cflags-$(CONFIG_MPENTIUMII) += $(call cc-option,/
-march=pentium2,-march=i686)
...
# Disable unit-at-a-time mode ...
CFLAGS += $(call cc-option,-fno-unit-at-a-time)
...
第一个例子利用了一个配置选项,当其为'y'时,扩展。
CFLAGS_KERNEL 编译进内核时,$(CC) 所用的选项
$(CFLAGS_KERNEL) 包含了用于编译常驻内核代码的附加编译器选项。
CFLAGS_MODULE 编译成模块时,$(CC)所用的选项
$(CFLAGS_MODULE) 包含了用于编译可装载模块的附加编译器选项。
--- 6.2 将所需文件加到 archprepare 中:
archprepare规则在递归访问子目录以前,列出编译目标文件所需文件。
通常状况下,这是一个包含汇编常量的头文件。(assembler constants)
例子:
#arch/arm/Makefile
archprepare: maketools
此例中,目标文件 maketools 将在递归访问子目录以前编译。
在TODO一章能够看到,Kbuild是如何支持生成分支头文件的。
(offset header files)
--- 6.3 递归下向时要访问的目录列表
如何生成 vmlinux,是由架构makefile和顶层Makefile一块儿来定义的。注意,
架构Makefile是不会定义与模块相关的内容的,全部构建模块的定义是与架构
无关的。
head-y,init-y,core-y,libs-y,drivers-y,net-y
$(head-y) 列出了最早被联接进 vmlinux 的目标文件。
$(libs-y) 列出了生成的全部 lib.a 所在的目录。
其他所列的目录,是 built-in.o 所在的目录。
$(init-y) 在 $(head-y) 以后所要使用的文件。
而后,剩下的步骤以下:
$(core-y),$(libs-y),$(drivers-y)和$(net-y)。
顶层makefile定义了通用的部分,arch/$(ARCH)/Makefile 添加了架构的特殊
要求。
例子:
#arch/sparc64/Makefile
core-y += arch/sparc64/kernel/
libs-y += arch/sparc64/prom/ arch/sparc64/lib/
drivers-$(CONFIG_OPROFILE) += arch/sparc64/oprofile/
--- 6.4 具体架构的启动镜像
一具体架构Makefile的具体目的就是,将生成并压缩 vmlinux 文件,写入启动
代码,并将其拷贝到正确的位置。这就包含了多种不一样的安装命令。该具体目的
也没法在各个平台间进行标准化。
通常,附加的处理命令入在 arch/$(ARCH)/下的boot目录。
Kbuild并无为构造boot所指定的目标提供任何更好的方法。因此,
arch/$(ARCH)/Makefile 将会调用 make 以手工构造 boot的目标文件。
比较好的方法是,在 arch/$(ARCH)/Makefile 中包含快捷方式,并在
arch/$(ARCH)/boot/Makefile 中使用所有路径。
例子:
#arch/i386/Makefile
boot := arch/i386/boot
bzImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
当在子目录中调用 make 时,推荐使用 "$(Q)$(MAKE) $(build)=<dir>" 。
并无对架构特殊目标的命名规则,但用命令 "make help" 能够列出全部的
相关目标。
为了支持 "make help",$(archhelp) 必须被定义。
例子:
#arch/i386/Makefile
define archhelp
echo '* bzImage - Image (arch/$(ARCH)/boot/bzImage)'
endef
当make 没带参数执行时,所遇到的第一个目标将被执行。在顶层,第一个目标
就是 all:。
每一个架构Makefile都要默认构造一可启动的镜像文件。
在 "make help"中,默认目标就是被加亮的'*'。
添加一新的前提文件到 all:,就能够构造出一不一样的vmlinux。
例子:
#arch/i386/Makefile
all: bzImage
当 make 没有参数时,bzImage将被构造。
--- 6.5 构造非Kbuild目标
extra-y
extra-y 列出了在当前目录下,所要建立的附加文件,不包含任何已包含在
obj-* 中的文件。
用 extra-y 列目标,主要是两个目的:
1) 可使Kbuild检查命令行是否发生变化
- 使用 $(call if_changed,xxx) 的时候
2) 让Kbuild知道哪些文件要在 "make clean" 时删除
例子:
#arch/i386/kernel/Makefile
extra-y := head.o init_task.o
在此例子中,extra-y用来列出全部只编译,但不联接到 built-in.o的目标
文件。
--- 6.6 构建启动镜像的命令
Kbuild 提供了几个用在构建启动镜像时的宏。
if_changed
if_changed 为下列命令的基础。
使用方法:
target: source(s) FORCE
$(call if_changed,ld/objcopy/gzip)
当执行该规则时,就检查是否有文件须要更新,或者在上次调用之后,命令行
发生了改变。若是有选项发生了改变,后者会致使从新构造。
只有在 $(targets)列出的的目标文件,才能使用 if_changed,不然命令行的
检查会失败,而且目标总会被重建。
给 $(targets)的赋值没有前缀 $(obj)/ 。 if_changed 可用来联接自定义的
Kbuild命令,关于Kbuild自定义命令请看 6.7节。
注意:忘记 FORCE 是一种典型的错误。还有一种广泛的错误是,空格有的时候
是有意义的;好比。下面的命令就会错误(注意在逗号后面的那个多余的空格):
target: source(s) FORCE
#WRONG!# $(call if_changed, ld/objcopy/gzip)
ld
联接目标。常常是使用LDFLAGS_$@来设置ld的特殊选项。
objcopy
拷贝二进制代码。通常是在 arch/$(ARCH)/Makefile 中使用 OBJCOPYFLAGS。
OBJCOPYFLAGS_$@ 能够用来设置附加选项。
gzip
压缩目标文件。尽量的压缩目标文件。
例子:
#arch/i386/boot/Makefile
LDFLAGS_bootsect := -Ttext 0x0 -s --oformat binary
LDFLAGS_setup := -Ttext 0x0 -s --oformat binary -e begtext
targets += setup setup.o bootsect bootsect.o
$(obj)/setup $(obj)/bootsect: %: %.o FORCE
$(call if_changed,ld)
在这个例子中,有两个可能的目标文件,分别要求不一样的联接选项。定义联接
器的选项使用的是 LDFLAGS_$@ 语法,每一个潜在的目标一个。
$(targets) 被分配给全部的潜在目标,所以知道目标是哪些,而且还会:
1) 检查命令行是否改变
2) 在 "make clean" 时,删除目标文件
前提部分中的 ": %: %.o" 部分使咱们没必要在列出文件 setup.o 和
bootsect.o 。
注意:一个广泛的错误是忘记了给 "target"赋值,致使在target中的文件老是
平白无故的被从新编译。
--- 6.7 Kbuild自定义命令
当Kbuild的变量 KBUILD_VERBOSE 为0时,只会显示命令的简写。
若是要为自定义命令使用这一功能,须要设置2个变量:
quiet_cmd_<command> - 要显示的命令
cmd_<command> - 要执行的命令
例子:
#
quiet_cmd_image = BUILD $@
cmd_image = $(obj)/tools/build $(BUILDFLAGS) /
$(obj)/vmlinux.bin > $@
targets += bzImage
$(obj)/bzImage: $(obj)/vmlinux.bin $(obj)/tools/build FORCE
$(call if_changed,image)
@echo 'Kernel: $@ is ready'
当用"make KBUILD_VERBOSE=0"更新 $(obj)/bzImage 目标时显示:
BUILD arch/i386/boot/bzImage
--- 6.8 联接器预处理脚本
当构造 vmlinux 镜像时,使用联接器脚本:
arch/$(ARCH)/kernel/vmlinux.lds。
该脚本是由在同一目录下的 vmlinux.lds.S 生成的。
Kbuild认识.lds文件,并包含由*.lds.S文件生成*.lds文件的规则。
例子:
#arch/i386/kernel/Makefile
always := vmlinux.lds
#Makefile
export CPPFLAGS_vmlinux.lds += -P -C -U$(ARCH)
$(always)的值是用来告诉Kbuild,构造目标 vmlinux.lds。
$(CPPFLAGS_vmlinux.lds),Kbuild在构造目标vmlinux.lds时所用到的特殊
选项。
当构造 *.lds 目标时,Kbuild要用到下列变量:
CPPFLAGS : 在顶层目录中设置
EXTRA_CPPFLAGS : 能够在Kbuild Makefile中设置
CPPFLAGS_$(@F) : 目标特别选项
注意,此处的赋值用的完整的文件名。
针对*.lds文件的Kbuild构架还被用在许多具体架构的文件中。(***不通***)
=== 7 Kbuild 变量
顶层Makefile输出如下变量:
VERSION,PATCHLEVEL,SUBLEVEL,EXTRAVERSION
这些变量定义了当前内核的版本号。只有不多一部分Makefile会直接用到这些
变量;可以使用 $(KERNELRELEASE)代替。
$(VERSION),$(PATCHLEVEL),和$(SUBLEVEL) 定义了最初使用的三个数字的版本
号,好比"2""4"和"0"。这三个值通常是数字。
$(EXTRAVERSION) 为了补丁定义了更小的版本号。通常是非数字的字符串,好比
"-pre4" ,或就空着。
KERNELRELEASE
$(KERNELRELEASE) 是一个字符串,相似"2.4.0-pre4",用于安装目录的命名或
显示当前的版本号。一部分架构Makefile使用该变量。
ARCH
该变量定义了目标架构,好比"i386","arm" 或"sparc"。有些Kbuild Makefile
根据 $(ARCH) 决定编译哪些文件。
默认状况下,顶层Makefile将其设置为本机架构。若是是跨平台编译,用户能够
用下面的命令覆盖该值:
make ARCH=m68k ...
INSTALL_PATH
该变量为架构Makefile定义了安装内核镜像与 System.map 文件的目录。
主要用来指明架构特殊的安装路径。
INSTALL_MOD_PATH,MODLIB
$(INSTALL_MOD_PATH) 为了安装模块,给 $(MODLIB) 声明了前缀。该变量不能
在Makefile中定义,但能够由用户传给Makefile。
$(MODLIB) 具体的模块安装的路径。顶层Makefile将$(MODLIB)定义为
$(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)。用户能够经过命令行
参数的形式将其覆盖。
INSTALL_MOD_STRIP
若是该变量有定义,模块在安装以前,会被剥出符号表。若是
INSTALL_MOD_STRIP 为 "1",就使用默认选项 --strip-debug。不然,
INSTALL_MOD_STRIP 将做为命令 strip 的选项使用。
=== 8 Makefile语言
内核的Makefile使用的是GNU Make。该Makefile只使用GNU Make已注明的功能,并使用
了许多GNU 的扩展功能。
GNU Make支持基本的显示处理过程的函数。内核Makefile 使用了一种相似小说的方式
,显示"if"语句的构造、处理过程。
GNU Make 有2个赋值操做符,":="和"="。":=",将对右边的表达式求值,并将所求的值
赋给左边。"="更像是一个公式定义,只是将右边的值简单的赋值给左边,当左边的表达
式被使用时,才求值。
有时使用"="是正确的。可是,通常状况下,推荐使用":="。
=== 9 关于做者
初版由 Michael Elizabeth Chastain,<mailto:mec@shout.NET>
修改:kai Germaschewski <kai@tpl.ruhr-uni-bochum.de>
Sam Ravnborg <sam@ravnborg.org>
=== 10 TODO
- 描述Kbuild是如何用 _shipped 来支持 shipped 文件的。
- 生成分支头文件
- 在第7节加入更多的变量 linux