菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
20
0

Kbuild文件

原创
05/13 14:22
阅读数 94884

3 Kbuild文件

大部分内核中的Makefile都是使用Kbuild组织结构的Kbuild Makefile。这章将介绍Kbuild Makefile的语法。

对于Kbuild文件名来讲,Kbuild编译系统更倾向于使用"Makefile"这个名字,当然"Kbuild"也是可以用的。但如果"Makefile"和"Kbuild"同时存在的话,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)列表中。而这些列表依赖内核的配置(.config)。

    Kbuild编译所有的$(obj-y)文件。然后,调用"$(LD) -r"将它们合并到一个build-in.o文件中。稍后,该build-in.o会被其父Makefile链接进vmlinux中。

$(obj-y)列表中的文件是有顺序讲究的。而且$(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 recognises objects used for composite objects by the suffix -objs, and the suffix -y。

This allows the Makefiles to use the value of a CONFIG_ symbol to determine if an object is part

of a composite object.    

例子:

          # 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 要创建,库文件lib.a所在的目录需要加到 libs-y 列表中。

还可参考"6.3 递归向下时要访问的目录列表"。   

lib-y 的使用一般限制在 lib/ 和 arch/*/lib 中。

3.6 递归向下访问目录

    其实,一个Makefile只负责编译其所在目录下的对象文件。在子目录中的文件的编译要由其所在的子目录的Makefile来管理。只要你让Kbuild系统知道它应该递归操作,那么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来决定。

It is good practice to use a CONFIG_ variable when assigning directory    names. This allows kbuild to totally skip the directory if the corresponding CONFIG_ option is neither 'y' nor 'm'.

3.7 编辑标志

    EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS

    所有的EXTRA_ 变量只在其被定义的Kbuild Makefile文件中起作用。而且,EXTRA_ 变量可以在Kbuild Makefile中的所有命令中使用。

l          $(EXTRA_CFLAGS) 是用 $(CC) 编译C源文件时要用到的选项。

    例子:

          # drivers/sound/emu10kl/Makefile

          EXTRA_CFLAGS += -I$(obj)

          ifdef DEBUG

              EXTRA_CFLAGS += -DEMU10KL_DEBUG

          endif

EXTRA_CFLAGS变量是必须的,因为顶层Makefile使用 $(EXTRA_CFLAGS)作为整个源代码树的编译选项。

l          $(EXTRA_AFLAGS) 也是一个针对每个目录的选项,只不过它是用来编译汇编源代码的。

    例子:

        # arch/x86_64/kernel/Makefile

        EXTRA_AFLAGS := -traditional

l          $(EXTRA_LDFLAGS) 和 $(EXTRA_ARFLAGS)分别与 $(LD)和 $(AR)类似,只不过,他们是针对内核的每个目录的编辑标志。

    例子:

        # arch/m68k/fpsp040/Makefile

        EXTRA_LDFLAGS := -x

l          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的编译选项。

l          $(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编译系统不能提供所要求的支持时,所使用的规则。一个典型的例子就是在构建过程中生成的头文件。Another example are the architecture-specific Makefiles which need special rules to prepare boot images etc.

Special rules are written as normal Make rules.

         Kbuild is not executing in the directory where the Makefile is located, so all special rules shall provide a relative     path to prerequisite files and target files.

         Two variables are used when defining special rules:

l          $(src)

    $(src) 表明Makefile所在目录的相对路径。需要定位源代码树中的文件时经常使用该变量。

l          $(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。

l          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就会被赋予该值。    第二个参数是可选的,当第一个参数不支持时,就会使用该值。

l          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就会被赋予该值。

    第二个参数是可选的,当第一个参数不支持时,就会使用该值。

l          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 不会被赋值。

l          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。

l          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 。

l          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的时候使用。

l          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。

    第三个参数可以像上面例子一样是个文本,但也可以是个扩展的变量或宏。

发表评论

0/200
20 点赞
0 评论
收藏
为你推荐 换一批