|
回复: linux2.6内核Makefile详解
例子:
#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节加入更多的变量 |
|