现在的位置: 首页 > 综合 > 正文

2.6内核makefile详解 documention/kbuild/makefiles.txt—翻译

2017年05月18日 ⁄ 综合 ⁄ 共 22143字 ⁄ 字号 评论关闭

linux2.6内核Makefile详解

熟悉内核的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提供其架构的特别信息。

 

每一个子目录都有一个KbuildMakefile文件,用来执行从其上层目录传递下来的命令。

Kbuild Makefile从.config文件中提取信息,生成Kbuild完成内核编译所需的文件列表。

 

scripts/Makefile.*包含了所有的定义、规则等信息。这些文件被用来编译基于kbuild

Makefile的内核。(**有点不通**)

 

=== 2 用户与作用

 

可以将人们与内核Makefile的关系分成4类。

 

*使用者* 编译内核的人。他们只是键入"make menuconfig"或"make"这样的命令。一般

情况下是不会读或编辑任何内核Makefile(或者任何的源文件)。

 

*普通开发人员* 这是一群工作在内核某一功能上的人,比如:驱动开发,文件系统或

网络协议。他们所需要维护的只是他们所工作的子系统的Kbuild Makefile。为了提高

工作的效率,他们也需要对内核Makefile有一个全面的认识,并且要熟悉Kbuild的接口

 

*架构开发人员* 这是一些工作在具体架构,比如sparc 或者ia64,上面的人。架构开

发者需要在熟悉kbuildMakefile的同时,也要熟悉他所工作架构的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

       &n

bsp;  # Makefile for the kernel ISDN subsystem anddevice drivers.

          # Each configuration option enables alist 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.oisdn_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 += $(callas-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 += $(callcc-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

bsp;   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>,二者都是数学。比如,gcc3.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) 在一具体的规则中显性列出所需要的文件

 

    例子:

        #dr

ivers/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)

 

"makeclean"删除几乎所有的在编译内核时生成的文件,包括了主机程序在内。

Kbuild 通过列表 $(hostprogs-y),$(hostprogs-m),$(always),$(extra-y) 和

$(targets) 知道所要编译的目标。这些目标文件都会被 "makeclean" 删除。另外

,在"makeclean"还会删除匹配 "*.[oas]","*.ko" 的文件,以及由 Kbuild生成

的辅助文件。

 

辅助文件由 KbuildMakefile 中的 $(clean-files) 指明。

 

    例子:

        #drivers/pci/Makefile

        clean-files  := devlist.h classlist.h

 

当执行"make clean" 时,"devlist.hclasslist.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

 

当"makeclean"执行时,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 += $(callcc-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 += $(callcc-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 -ebegtext

 

        targets += setup setup.o bootsectbootsect.o

        $(obj)/setup $(obj)/bootsect: %: %.oFORCE

            $(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 关于作者

第一版由 MichaelElizabeth Chastain,<mailto:mec@shout.net>

修改:kaiGermaschewski <kai@tpl.ruhr-uni-bochum.de>

      Sam Ravnborg <sam@ravnborg.org>

 

=== 10 TODO

 

- 描述Kbuild是如何用_shipped 来支持 shipped 文件的。

- 生成分支头文件

- 在第7节加入更多的变量

 

抱歉!评论已关闭.