读Kernel感悟-kbuild系统-make bzImage的过程

来源:互联网 发布:建筑设计软件有哪些 编辑:程序博客网 时间:2024/05/16 15:25

 

Kernel感悟-kbuild系统-make bzImage的过程

 

从以上例子中可以看到,内核的编译系统kbuild是个很庞大的系统。但是,它所使用的make和我们平时用的make是一模一样的。kbuild只是通过预定义一些变量(obj-m,obj-y等等)和目标(bzImage ,menuconfig等等),使内核的编译和扩展变得十分方便。我们不妨yy一下kbuild的一些功能:

1.考虑到Linux能够方便地移植到各个硬件平台,kbuild也必须很容易添加对某个新的平台的支持,同时上层的Makefile不需要做大的改动。

2.Linux下有众多驱动设备。它们的Makefile希望能够尽可能简洁。简洁到只要指定要编译的.o文件就行。(这方面kbuild定义了很多有用的变量如obj-m obj-y,<module>-objs等等,用户只要为这些变量赋值,kbuild会自动把代码编译到内核或者编译成模块)

3.要有方便的可定制性。很多参数可以让用户指定。这方面kbuild也提供了大量的变量如EXTRA_CFLAGS,用户如果想include自己的头文件或者加其它编译参数,只要设置一下EXTRA_CFLAGS就可以。

4.有能力递归地调用Makefile。因为内核是一个庞大的软件。它的源代码的目录层次很深。要提供一种简洁的机制,使上层的Makefile能方便地调用下层的Makefile。在这过程中,面向对象的思想也许值得借鉴。

5.在配置内核时,要提供友好的用户界面。这方面kbuild也提供了不少工具,如常用的make menuconfig等等。

我们完全可以把kbuild想象成一个类库,它为普通的内核开发人员提供了接口(obj-m obj-y EXTRA_CFLAGS等等),为用户提供了定制工具(make menuconfig

如果想了解kbuild的使用方法,可以参阅源代码自带的文档:

Documentation/kbuild/makefiles.txt

Documentation/kbuild/modules.txt

一般情况下是不需要知道具体的编译顺序的。除了在个别情况下,如do_initcalls()中就和函数在.initcall.init section中的顺序有关。不过喜欢寻根究底的我,还是想理一下编译内核时几个常用的命令,如make bzImage,make menuconfig等等,进而了解kbuild的架构。先看make bzImage吧。

它的大概脉络是怎样的呢?可以用以下命令查看。

make -n bzImage

如果嫌内容太多,可以过滤掉多余的信息:

make -n bzImage | grep “make -f”

可以猜到:

先作一些准备工作

make -f scripts/Makefile.build obj=scripts/basic

然后依次递归地调用源代码中的Makefile

make -f scripts/Makefile.build obj=init

 

make -f scripts/Makefile.build obj=usr

 

make -f scripts/Makefile.build obj=arch/i386/kernel

 

make -f scripts/Makefile.build obj=arch/i386/kernel/acpi

 

make -f scripts/Makefile.build obj=arch/i386/kernel/cpu

 

make -f scripts/Makefile.build obj=arch/i386/kernel/cpu/cpufreq

 

make -f scripts/Makefile.build obj=arch/i386/kernel/cpu/mcheck

 

make -f scripts/Makefile.build obj=arch/i386/kernel/cpu/mtrr

 

make -f scripts/Makefile.build obj=arch/i386/kernel/timers

 

。。。

最后压缩内核,生成bzImage

make -f scripts/Makefile.build obj=arch/i386/boot arch/i386/boot/bzImage

 

make -f scripts/Makefile.build obj=arch/i386/boot/compressed IMAGE_OFFSET=0x100000 arch/i386/boot/compressed/vmlinux

 

 

 

好,我们从头开始。找make bzImage的入口:

第一反应,自然是在/usr/src/linux/Makefile中找

bzImage:

...

可惜没找到。

不过没关系,用lxr搜索一下,可知bzImage定义在arch/i386/Makefile,所以可以猜测,该makefile一定是被include了。果然,在/usr/src/linux/Makefile中有:

447 include $(srctree)/arch/$(ARCH)/Makefile

又因为在arch/i386/Makefile中定义有

141 zImage bzImage: vmlinux

142        $(Q)$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE)

 

其中这个$(build)定义在/usr/src/linux/Makefile

1335 build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj

 

我们在之前查看make -n bzImage信息和之后会经常看到。我们会发现kbuild通常不会直接去调用某个目录下的Makefile,而是让该目录作为scripts/Makefile.build的参数。scripts/Makefile.build会对该目录下的Makefile中的内容(主要是obj-mobj-y等等)进行处理。由此看来 scripts/Makefile.build这个文件很重要。看看它做了什么:

 

 

了解更多内容请点击:http://www.top-e.org/jiaoshi/html/?251.html

 

 

 

 

Kernel感悟-kbuild系统-make bzImage的过程12183100201

2009-4-28 13:09:11   收藏  | 打印  | 投票  |  评论  | 阅读  字体:[  

从以上例子中可以看到,内核的编译系统kbuild是个很庞大的系统。但是,它所使用的make和我们平时用的make是一模一样的。kbuild只是通过预定义一些变量(obj-m,obj-y等等)和目标(bzImage ,menuconfig等等),使内核的编译和扩展变得十分方便。我们不妨yy一下kbuild的一些功能:

1.考虑到Linux能够方便地移植到各个硬件平台,kbuild也必须很容易添加对某个新的平台的支持,同时上层的Makefile不需要做大的改动。

2.Linux下有众多驱动设备。它们的Makefile希望能够尽可能简洁。简洁到只要指定要编译的.o文件就行。(这方面kbuild定义了很多有用的变量如obj-m obj-y,-objs等等,用户只要为这些变量赋值,kbuild会自动把代码编译到内核或者编译成模块)

3.要有方便的可定制性。很多参数可以让用户指定。这方面kbuild也提供了大量的变量如EXTRA_CFLAGS,用户如果想include自己的头文件或者加其它编译参数,只要设置一下EXTRA_CFLAGS就可以。

4.有能力递归地调用Makefile。因为内核是一个庞大的软件。它的源代码的目录层次很深。要提供一种简洁的机制,使上层的Makefile能方便地调用下层的Makefile。在这过程中,面向对象的思想也许值得借鉴。

5.在配置内核时,要提供友好的用户界面。这方面kbuild也提供了不少工具,如常用的make menuconfig等等。

我们完全可以把kbuild想象成一个类库,它为普通的内核开发人员提供了接口(obj-m obj-y EXTRA_CFLAGS等等),为用户提供了定制工具(make menuconfig

如果想了解kbuild的使用方法,可以参阅源代码自带的文档:

Documentation/kbuild/makefiles.txt

Documentation/kbuild/modules.txt

一般情况下是不需要知道具体的编译顺序的。除了在个别情况下,如do_initcalls()中就和函数在.initcall.init section中的顺序有关。不过喜欢寻根究底的我,还是想理一下编译内核时几个常用的命令,如make bzImage,make menuconfig等等,进而了解kbuild的架构。先看make bzImage吧。

它的大概脉络是怎样的呢?可以用以下命令查看。

make -n bzImage

如果嫌内容太多,可以过滤掉多余的信息:

make -n bzImage | grep “make -f”

可以猜到:

先作一些准备工作

make -f scripts/Makefile.build obj=scripts/basic

然后依次递归地调用源代码中的Makefile

make -f scripts/Makefile.build obj=init

 

make -f scripts/Makefile.build obj=usr

 

make -f scripts/Makefile.build obj=arch/i386/kernel

 

make -f scripts/Makefile.build obj=arch/i386/kernel/acpi

 

make -f scripts/Makefile.build obj=arch/i386/kernel/cpu

 

make -f scripts/Makefile.build obj=arch/i386/kernel/cpu/cpufreq

 

make -f scripts/Makefile.build obj=arch/i386/kernel/cpu/mcheck

 

make -f scripts/Makefile.build obj=arch/i386/kernel/cpu/mtrr

 

make -f scripts/Makefile.build obj=arch/i386/kernel/timers

 

。。。

最后压缩内核,生成bzImage

make -f scripts/Makefile.build obj=arch/i386/boot arch/i386/boot/bzImage

 

make -f scripts/Makefile.build obj=arch/i386/boot/compressed IMAGE_OFFSET=0x100000 arch/i386/boot/compressed/vmlinux

 

 

 

好,我们从头开始。找make bzImage的入口:

第一反应,自然是在/usr/src/linux/Makefile中找

bzImage:

...

可惜没找到。

不过没关系,用lxr搜索一下,可知bzImage定义在arch/i386/Makefile,所以可以猜测,该makefile一定是被include了。果然,在/usr/src/linux/Makefile中有:

447 include $(srctree)/arch/$(ARCH)/Makefile

又因为在arch/i386/Makefile中定义有

141 zImage bzImage: vmlinux

142        $(Q)$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE)

 

其中这个$(build)定义在/usr/src/linux/Makefile

1335 build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj

 

我们在之前查看make -n bzImage信息和之后会经常看到。我们会发现kbuild通常不会直接去调用某个目录下的Makefile,而是让该目录作为scripts/Makefile.build的参数。scripts/Makefile.build会对该目录下的Makefile中的内容(主要是obj-mobj-y等等)进行处理。由此看来 scripts/Makefile.build这个文件很重要。看看它做了什么:

由于scripts/Makefile.build后面没跟目标,所以默认为第一个目标:

007 .PHONY: __build

 

008 __build:

 

009

 

010 # Read .config if it exist, otherwise ignore

 

011 -include .config

 

012

 

013 include $(if $(wildcard $(obj)/Kbuild), $(obj)/Kbuild, $(obj)/Makefile)

 

014

 

015 include scripts/Makefile.lib

 

 

这里可以看到,scripts/Makefile.build执行时会include .config文件。.configmake menuconfig后生成的内核配置文件。

里面有如下语句:

CONFIG_ACPI_THERMAL=y

 

CONFIG_ACPI_ASUS=m

 

CONFIG_ACPI_IBM=m

 

。。。

以前我一直对它的格式表示奇怪,现在很清楚了,它们是作为makefile的一部分,通过读取CONFIG_XXX的值就可以知道他们是作为模块还是作为内核的一部分而编译的。

此外,还包含了$(obj)/Makefile。这就是通过在make时传递目录名$(obj)间接调用Makefile的手法。对于内核普通代码所对应的Makefile而言,里面只是对obj-m obj-y,-objs等变量进行赋值操作。

接下去是include scripts/Makefile.lib

。正如它的文件名所示,这类似于一个库文件。它负责对obj-m obj-y,-objs等变量进行加工处理。从中提取出subdir-ym等变量,这是个很重要的变量,记录了需要递归调用的子目录。以后递归调用Makefile全靠它了。这里也充分体现了GNU make对字符串进行操作的强大功能。

回到Makefile.build。这时,重要变量$(builtin-target)$(subdir-ym)等都已经计算完毕。开始列依赖关系和具体操作了。

079 __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) /

 

080         $(if $(KBUILD_MODULES),$(obj-m)) /

 

081         $(subdir-ym) $(always)

 

082        @:

 

 

 

$(builtin-target)是指当前目录下的目标文件,即$(obj)/built-in.o

如前文所说,$(subdir-ym)用来递归调用子目录的Makefile

306 # Descending

 

307 # ---------------------------------------------------------------------------

 

308

 

309 .PHONY: $(subdir-ym)

 

310 $(subdir-ym):

 

311        $(Q)$(MAKE) $(build)=$@

 

 

通过这种方式,实现了对某个目录及其子目录的编译。

 

分析完Makefile.build,回过头来再看bzImage.arch/i386/Makefile中可以看到,bzImage是在vmlinux基础上加以压缩拼接而成。从vmlinuxbzImage的过程在《读核感悟-Linux内核启动-内核的生成》中已经有介绍。现在看看vmlinux是如何生成的。

/usr/src/linux/Makefile

728 vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE

 

729        $(call if_changed_rule,vmlinux__)

 

 

 

611 vmlinux-init := $(head-y) $(init-y)

 

612 vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)

 

613 vmlinux-all := $(vmlinux-init) $(vmlinux-main)

 

614 vmlinux-lds := arch/$(ARCH)/kernel/vmlinux.lds

 

 

vmlinux所依赖的目标$(vmlinux-lds)是对arch/i386/kernel/vmlinux.lds.S进行预处理的结果:arch/i386/kernel/vmlinux.lds ,其它的依赖关系也都可以在/usr/src/linux/Makefile中查到。

所以,当用户在源代码目录下执行make bzImagemake会检查bzImage的依赖目标,然后不停地递归调用各个Makefile,最终生成一个bzImage文件。

如果我们换个角度,还可以归纳出不少有趣的东西。如果把make看成是一种脚本语言,那么Makefile就是代码。make就是解释器。make里也有函数,也有变量。通过定义目标,可以实现类似于函数的效果。而目标之间的依赖关系则类似于函数内部再调用其它函数。

如果我们考虑变量的作用域,还可以归纳出以下几点:

1.Makefile内部定义的变量作用域只限于那个Makefile中,如obj-m

2.要使变量的作用域扩展到整个make命令的执行过程(包括递归调用的其它Makefile),需要使用export命令。

调用Makefile的方式也有很多种:

1.一种是隐式调用,如运行make,它会自动在当前目录寻找Makefile等。

2.一种是显式调用,如用make -f指定。

3.一种是用include来调用。

 

 

如何确定Linux内核源代码目录即,$KBUILD的路径

方法一:

确定内核源代码目录通常==文件系统中内核驱动模块的build路径

 

/lib/modules/2.6.25-14.fc9.i686/build,这个build通常为链接文件,连接到

/usr/src/kernels/2.6.25-14.fc9.i686

此方法较准确,通常可以写如下脚本实现:

# KBUILD is the path to the Linux kernel build tree. It is usually the

# same as the kernel source tree, except when the kernel was compiled in

# a separate directory.

KBUILD ?= $(shell readlink -f /lib/modules/$(KVERS)/build)

 

方法二:

自己下载内核源文件包,自己指定内核的编译目录!

不推荐这种做法,还是按照各大发行版的做法比较好!这样不至于在编译下载的某个设备驱动程序时

给自己带来不必要的麻烦!

 

文章出处:http://www.diybl.com/course/6_system/linux/Linuxjs/2008624/128067.html

 

 

 

 

 

 

2.6内核Makefile简单语法与应用

1.1概述
2.6
Makefile的写法和应用相对于2.4有了一些变化,可能对于很多人来说,因为找不到相关的文档,都是模仿内核中已有的文件来写自己的Makefile。其实,在内核的Documentation / kbuild目录下面,还是有对内核Makefile语法的详细说明的。在这里就2.6内核中Makefile最常见的简单应用情况做一个翻译和归纳介绍。

2.6
内核的Makefile分为5个组成部分:

l
最顶层的Makefile
l
内核的.config配置文件
l
arch/$(ARCH)目录下的体系结构相关的Makefile
l
scripts/目录下的 Makefile.*文件,是一些Makefile的通用规则
l
各级目录下的大概约500kbuild Makefile文件

顶层的Makefile文件读取 .config文件的内容,并总体上负责build内核和模块。Arch Makefile则提供补充体系结构相关的信息。 Scripts目录下的Makefile文件包含了所有用来根据kbuild Makefile 构建内核所需的定义和规则。
1.2 Kbuild Makefile
对于Makefiles的不同组成部分,有一些不同的语法规则。针对的对象也不同,对于大部分内核模块或设备驱动的开发者和使用者来说,最常接触到的就是各层目录下基于kbuild架构的kbuild Makefile文件。
Kbuild Makefile
的语法结构非常简单,核心内容主要包括
1.2.1
目标定义
目标定义就是用来定义哪些内容要做为模块编译,哪些要编译链接进内核。
例如
obj-y += foo.o
表示要由foo.c或者foo.s文件编译得到foo.o并链接进内核,而obj-m则表示该文件要作为模块编译。除了ym以外的obj-x形式的目标都不会被编译。
而更常见的做法是根据.config文件的CONFIG_变量来决定文件的编译方式,如:
obj-$(CONFIG_ISDN) += isdn.o
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o

除了obj-形式的目标以外,还有lib-y library库,hostprogs-y主机程序等目标,但是基本都应用在特定的目录和场合下。
1.2.2
多文件模块的定义
最简单的kbuild Makefile如上一节一句话的形式就够了,如果一个模块由多个文件组成,那么稍微复杂一些,采用模块名加 –objs后缀或者 –y后缀的形式来定义模块的组成文件。如以下例子:
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
模块的名字为ext2,由balloc.obitmap.o两个目标文件最终链接生成ext2.o直至ext2.ko文件,是否包括xattr.o取决于内核配置文件的配置情况。如果CONFIG_EXT2_FS的值是y也没有关系,在此过程中生成的 ext2.o将被链接进built-in.o最终链接进内核。这里需要注意的一点是,该kbuild Makefile所在的目录中不应该再包含和模块名相同的源文件如ext2.c/ext2.s

或者写成如-objs的形式:
obj-$(CONFIG_ISDN) += isdn.o
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
1.2.3
目录层次的迭代
如下例:
obj-$(CONFIG_EXT2_FS) += ext2/
如果CONFIG_EXT2_FS的值为ymkbuild将会将ext2目录列入向下迭代的目标中,但是其作用也仅限于此,具体ext2目录下的文件是要作为模块编译还是链入内核,还是有ext2目录下的Makefile文件的内容来决定的。

1.2.4
模块的编译
编译模块的时候,你可以将模块放在代码树中,用Make modules的方式来编译你的模块,你也可以将模块相关文件目录放在代码树以外的位置,用如下命令来编译模块:
make -C path/to/kernel/src M=$PWD modules
-C
指定代码树的位置,M=$PWD M=`PWD`告诉kbuild回到当前目录来执行build操作。
1.2.5
模块的安装
当你需要将模块安装到非默认位置的时候,你可以用INSTALL_MOD_PATH指定一个前缀,如:
make INSTALL_MOD_PATH=/foo modules_install
模块将被安装到 /foo/lib/modules目录下。

 

 

 

 

 

 

 

 

八、KBuild MakeFile介绍

 

 

 Linux内核2.6开始,Linux内核的编译采用Kbuild系统,这同过去的编译系统有很大的不同,尤其对于Linux内核模块的编译。在新的系统下,Linux编译系统会两次扫描LinuxMakefile:首先编译系统会读取Linux内核顶层的Makefile,然后根据读到的内容第二次读取KbuildMakefile来编译Linux内核。

Linux内核Makefile分类

·        Kernel Makefile

Kernel Makefile位于Linux内核源代码的顶层目录,也叫 Top Makefile。它主要用于指定编译Linux Kernel目标文件(vmlinux)和模块(module)。这编译内核或模块是,这个文件会被首先读取,并根据读到的内容配置编译环境变量。对于内核或驱动开发人员来说,这个文件几乎不用任何修改。

·        Kbuild Makefile

Kbuild系统使用Kbuild Makefile来编译内核或模块。当Kernel Makefile被解析完成后,Kbuild会读取相关的Kbuild Makefile进行内核或模块的编译。Kbuild Makefile有特定的语法指定哪些编译进内核中、哪些编译为模块、及对应的源文件是什么等。内核及驱动开发人员需要编写这个Kbuild Makefile文件。

·        ARCH Makefile

ARCH Makefile位于ARCH/$(ARCH)/Makefile,是系统对应平台的MakefileKernel Top Makefile会包含这个文件来指定平台相关信息。只有平台开发人员会关心这个文件。


Kbuild Makefile
    Kbuild Makefile的文件名不一定是Makefile,尽管推荐使用Makefile这个名字。大多的Kbuild文件的名字都是Makefile。为了与其他Makefile文件相区别,你也可以指定Kbuild Makefile的名字为Kbuild。而且如果“Makefile”和“Kbuild”文件同时存在,则Kbuild系统会使用“Kbuild”文件。

·        目标定义

Kbuild Makefile的一个最主要功能就是指定编译什么,这个功能是通过下面两个对象指定的obj-?xxx-objs

·        obj-?

obj-?指定编译什么,怎么编译?其中的“?”可能是“y”或“m”,“y”指定把对象编译进内核中,“m”指定把对象编译为模块。语法如下;
    obj-? = $(target).o
target为编译对象的名字。如果没有指定xxx-objs,这编译这个对象需要的源文件就是$(target).c$(target).s。如果指定了$(target)-objs,则编译这个对象需要的源文件由$(target)-objs指定,并且不能有$(target).c$(target).s文件。

·        xxx-objs

xxx-objs指定了编译对象需要的文件,一般只有在源文件是多个时才需要它。

只要包含了这两行,Kbuild Makefile就应该可以工作了。

·        嵌套编译

有时一个对象可能嵌入到另一个对象的目录下,那个如何编译子目录下的对象呢?其实很简单,只要指定obj_?的对象为子目录的名字就可以了:

obj-? = $(sub_target)/

其中“?”可以是“y”或“m”,$(sub_target)是子目录名字。

·        编译器选项

尽管在大多数情况下不需要指定编译器选项,有时我们还是需要指定一些编译选项的。

·        ccflags-y, asflags-y and ldflags-y

这些编译选项用于指定ccasld的编译选项

编译外部模块
有时候我们需要在内核源代码数的外面编译内核模块,编译的基本命令是:

    make -C $(KERNEL_DIR) M=`pwd` modules
我们可以把这个命令集成到Makefile里,这样我们就可以只输入“make”命令就可以了。回想上一章的那个Makefile,它把Normal Makefile Kbuild  Makefile集成到一个文件中了。为了区别Kbuild Makefile Normal Makefile,这样我们改写Makefile为如下形式,并且添加Kbuild Makefile - Kbuild”。

##Makefile
ifneq ($(KERNELRELEASE),)
include "Kbuild"
else
KERNEL_DIR = /lib/modules/`uname -r`/build
MODULEDIR := $(shell pwd)

.PHONY: modules
default: modules

modules:
        make -C $(KERNEL_DIR)  M=$(MODULEDIR) modules

clean distclean:
        rm -f *.o *.mod.c .*.*.cmd *.ko
        rm -rf .tmp_versions
endif

 

## Kbuild
MODULE_NAME = helloworld
$(MODULE_NAME)-objs := hello.o
obj-m   := $(MODULE_NAME).o


一般不需要在Makefile里包含如下代码,这样写完全是为了兼容老版本的Kbuild系统。KERNELRELEASE变量在Kernel Makefile里定义的,因此只有在第二次由Kbuild读取这个Makefile文件时才会解析到Kbuild的内容。

ifneq ($(KERNELRELEASE),)
include "Kbuild"
else
...
endif


外部头文件
有时需要连接内核源代码外部的系统头文件,但Kbuild系统默认的系统头文件都在内核源代码内部,如何使用外部的头文件呢?这个可以借助于Kbuild系统的特殊规则:

·        EXTRA_CFLAGS

EXTRA_CFLAGS可以给Kbuild系统添加外部系统头文件,
    EXTRA_CFLAGS += $(ext_include_path)
一般外部头文件可能位于外部模块源文件的目录内,如何指定呢?这可以借助$(src)$(obj)

·        $(src)/$(obj)

$(src)是一个相对路径,它就是Makefile/Kbuild文件所在的路径。同样$(obj)就是编译目标保存的路径,默认就是源代码所在路径。


因此,我们修改Kbuild文件添加 EXTRA_CFLAGS 来包含外部头文件尽管在这个驱动里没有引用外部系统头文件:

## Kbuild
MODULE_NAME = helloworld
$(MODULE_NAME)-objs := hello.o
EXTRA_CFLAGS := -I$(src)/include
obj-m   := $(MODULE_NAME).o

 

 

 

·        Goal definitions

Example:

  obj-y += foo.o

告诉kbuild,在文件夹中又一个叫做foo.oobjectfoo.o将会被从foo.c或者foo.S被构建。

 

如果foo.o被构建成一个模块,则将使用变量obj-mExample:

  obj-$(CONFIG_FOO) += foo.o

$(CONFIG_FOO)要么是y(built-in)要么是m(module)。如果CONFIG_FOO既不是y也不是m,那么文件将不会被编译也不会被连接。

·        Built-in object goals - obj-y

kbuild Makefiles$(obj-y)列表中为vmlinux指明object文件。这个列表依靠内核的配置。

$(obj-y)中的文件的顺序是非常重要的。列表中允许两个相同的文件:第一个实体将被连接到built-in.o,后面的实体将会被忽略。

连接的顺序也很重要,因为在boot过程中某些函数(module_init()/_initcall)将会按顺序出现。因此,如果改变了连接顺序,将会改变你的SCSI控制器的检测顺序,你的磁盘也同时被重新编号了。

 Example:
  #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

·        Loadable module goals - obj-m

$(obj-m)指明object文件作为可装载的内核模块被构建。一个模块可能从一个或者多个源文件被构建。kbuild maefile只是简单的将源文件加到%(obj-m)

 Example:
  #drivers/isdn/i4l/Makefile
  obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o

 注意这里$(CONFIG_ISDN_PPP_BSDCOMP)m.

Note: In this example $(CONFIG_ISDN_PPP_BSDCOMP) evaluates to 'm'

如果一个内核模块从多个源文件构建,KBuild就必须要知道你想从哪些部分构建模块。因此,你不得不设置$(<module_name>-objs)变量来告诉KBuild

 Example:
  #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)object文件,然后在这些文件的列表中调用"$(LD) -r"来产生isdn.o

Kbuild使用后缀-objs,-y来识别混合的object文件。这允许Makefiles使用变量CONFIG_sambol来决定一个object是否是混合object的的一部分。

 Example:
  #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只是混合object文件ext2.o的一部分。

 注意,当你构造一个objects到内核中时,上面的语法当然也能够工作。因此,如果你让CONFIG_EXT2=Y,KBuild将会为你构建一个独立的ext2.o文件,并且连接到built-in.o

·        Library file goals - lib-y

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.olib.a文件。

Example:
  #arch/i386/lib/Makefile
  lib-y    := checksum.o delay.o

这里讲会创建一个基于checksum.odelay.o的库文件。对于kbuild,识别一个lib.a正在被构建,这个文件夹应该被列在libs-y中。lib-y的使用方法通常被限制在lib/arc/*/lib中。

·        Descending down in directories

一个Makefile只负责在他自己的文件夹中构建objects 在子文件夹中的文件应该由子文件夹中的Makefiles来照顾。如果你知道他们,build系统将会自动递归地用在子文件夹中的make

在这种情况下obj-yobj-m就被使用了。ext2存在于不同的文件夹中,Makefile出现在fs/,则告诉kbuild从后面的参数下来。

Example:
  #fs/Makefile
  obj-$(CONFIG_EXT2_FS) += ext2/

 如果CONFIG_EXT2_FS被设置成y(built-in)或者m(modular),相应的obj-变量将会被设置,并且kbuild将会从ext2文件夹继承下来。Kbuild只会使用这些信息来决定它需要访问这些文件夹,而在子文件夹中的Makefile来指明哪些是modules哪些是built-in

当赋值文件夹名字的时候,使用CONFIG_variable是很好的选择。这允许kbuild完全的跳过文件夹,而不管CONFIG_option是否是y或者m

·        Compilation flags

    EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS

所有的EXTRA_ variables只应用在kbuild中,他们被赋值的地方。EXTRA_variables应用在kbuild makefile中所有的可执行的命令。$(EXTRA_CFLAGS)指明用$(CC)编译C文件的时候的选项。

 Example:
  # drivers/sound/emu10k1/Makefile
  EXTRA_CFLAGS += -I$(obj)
  ifdef DEBUG
      EXTRA_CFLAGS += -DEMU10K1_DEBUG
  endif

这里的变量是必须的,因为顶层的Makefile拥有变量$(CFLAGS)并且用它来作为整个树的编译标志当编译汇编源文件的时候$(EXTRA_AFLAGS),和每个文件夹的选项是相似的。

 Example:
  #arch/x86_64/kernel/Makefile
  EXTRA_AFLAGS := -traditional

$(EXTRA_LDFLAGS)$(EXTRA_ARFLAGS)对于每个文件夹的$(LD)$(AR)选项是类似的。

 Example:
  #arch/m68k/fpsp040/Makefile
  EXTRA_LDFLAGS := -x

 CFLAGS_$@, AFLAGS_$@

 CFLAGS_$@AFLAGS_$@只应用到当前kbuild makefile的命令。

 $(CFLAGS_$@)为每个文件的$(CC)指明选项。$@
 部分有一个字面上的值,指明它是为那个文件。

 Example:
  # 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

 These three lines specify compilation flags for aha152x.o,
 gdth.o, and seagate.o

 $(AFLAGS_$@) is a similar feature for source files in assembly
 languages.

 Example:
  # arch/arm/kernel/Makefile
  AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR) -traditional
  AFLAGS_head-armo.o := -DTEXTADDR=$(TEXTADDR) -traditional

 原文地址http://blog.sina.com.cn/s/blog_48c1b149010002qj.html

 

 

 

 

标签:无标签

Kbuild Makefile简要总结

  Linux2.4内核中,模块的编译只需要内核源码头文件,并在包括linux/modules.h头文件之前定义MODULES,且其编译、连接后生成的内核模块后缀为.o。而在2.6内核中,模块的编译需要依赖配置过的内核源码,编译过程首先会到内核源码目标下,读取顶层的Makefile文件,且编译、连接后生成的内核模块后缀为.ko

        2.4内核模块Makefile模板

#Makefile for linux2.4

KVER=$(shell uname -r)

KDIR=/lib/modules/$(KVER)/build

OBJS=mymodule.o

CFLAGS=-D__KERNEL__ -I$(KDIR) /include -DMODULE 

  D__KERNEL_SYSCALLS__ -DEXPORT_SYSTAB -O2 -fomit-frame-pointer -Wall

  -DMOVERSIONS

all: $(OBJS)

  mymodule.o: file1.o file2.o

  ld -r -o $@ $^

clean:

  rm -f *.o

  Linux2.6内核中模块的Makefile模板

#Makefile for linux2.6

ifneq ($(KERNELRELEASE),)
# call from kernel build system

scull-objs := main.o pipe.o access.o

obj-m := scull.o

else

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)

modules:
 $(MAKE) -C $(KERNELDIR) M=$(PWD)

clean:
 rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions


endif

  Linux2.6内核模板Makefile中的KERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量,在第一次读取执行此MakefileKERNELRELEASE没有被定义,所以make将读取执行else之后的内容。如果make的目标是clean,将直接执行clean,然后结束;当make的目标为modules时, -C $(KERNELDIR)指明跳转到内核源码目标下读取那里的Makefile M=$(PWD)表明之后要返回到当前目标继续读入,执行当前的Makefile。当从内核源码目标返回到当前目录时,KERNELDIR已被定义,kbuild也被启动去解析kbuild语法的语句,make将继续读取else之前的内容。else之前为kbuild语法的语句,指明模块源码中各文件的依赖关系,以及要生成的目标模块名。scull-objs := main.o pipe.o access.o表示scull.o main.o pipe.o access.o连接生成。obj-m := scull.o表明编译连接后将生成scull模块。

  同时支持2.42.6内核的Makefile模板

#Makefile for 2.4 2.6

VERS26=$(findstring 2.6,$(shell uname -r))

MAKEDIR?=$(shell pwd)

ifeq($(VERS26),2.6)

include $(MAKEDIR)/Makefile2.6

else

include $(MAKEDIR)/Makefile2.4

endif

  此模板根据Linux的版本调用Makefile2.6Makefile2.4进行编译。因此还需要提供2.42.6Makefile

  完成如上2.6内核同样功能的Makefile还可以这样写

#Makefile simple for 2.6

obj-m := scull.o

scull-objs := main.o pipe.o access.o

  然后在包含Makefile和源码的目标中执行如下make命令(假设内核在~/kernel-2.6中)。

make -C ~/kernel-2.6 M='pwd' modules

  上述命令首先改变目标到-C选项指定的位置(即内核源代码目标),其中保存有内核的顶层makefile文件。M=选项让该makefile在构造modules目标之前返回到构造模块源代码目标。然后,modules目标指向obj-m变量中设定的模块,即scull.o

注:总结自《Linux设备驱动开发详解》第一版p668-p670及《Linux设备驱动程序》第三版p28-p30

 

 

 

 

 

linux2.6内核Makefile详解 []


[url=http://bbs.edw.com.cn/forum/post/id/609588]linux2.6
内核Makefile详解[/url]
[
]
http://forum.eepw.com.cn/forum/main?url=http%3A%2F%2Fbbs.edw.com.cn%2Fthread%2F128730%2F1
熟悉内核的Makefile对开发设备驱动、理解内核代码结构都是非常重要的
linux2.6
内核Makefile的许多特性和2.4内核差别很大,在内核目录的documention/kbuild/makefiles.txt中有详细的说明。给大家一个中文版的翻译
===
目录
        === 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
需要知道你所编译的模块是基于哪些文件,所以你需要通过变量
    $(-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.odelay.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 - = 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)编译器的版本号。
   
其格式是:,二者都是数学。比如,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
复合的本机程序
   
本机程序可以由多个文件编译而成。
   
所使用的语法与内核的相应语法很相似。
    $(-objs)
列出了联接成最后的可执行文件所需的所有目标文件。
   
例子:
        #scripts/lxdialog/Makefile
        hostprogs-y    := lxdialog
        lxdialog-objs    := checklist.o lxdialog.o
   
扩展名为.o的文件是从相应的.c文件编译而来的。在上面的例子中,
    checklist.c
编译成了checklist.olxdialog.c编译成了lxdialog.o
   
最后,两个.o文件联接成了一可执行文件,lxdialog
   
注意:语法 -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 ) "

   
并没有对架构特殊目标的命名规则,但用命令 "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_    -
要显示的命令
          cmd_    -
要执行的命令
   
例子:
        #
        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,
修改:kai Germaschewski
      Sam Ravnborg
=== 10 TODO
-
描述Kbuild是如何用 _shipped 来支持 shipped文件的。
-
生成分支头文件
-
在第7节加入更多的变量

 

 

 

 

 

 

 

 

 

 

 

原创粉丝点击