linux官方文档翻译 -- Makefile的语法

译者注:终于把这篇linux讲述makefile语法的文档翻译完了,它相对于上一篇翻译的《linux官方文档翻译--Kconfig的语法》长度要多了2倍。整个个翻译的过程持续了比较久,其间进行了照毕业照、吃散伙饭等毕业活动,因此耽搁了几天,总之现在是翻译完了。因为翻译的比较久,我翻到后面都忘记了前面讲的什么。现在对makefile语法的概念还比较模糊,看来还得把翻译的再看一遍。到现在翻译了两篇文档,感觉对阅读英文文档流畅了许多,作用还是挺大的。因此下面接着翻译,下一篇要翻译的是《linux官方文档翻译--modules的语法》,敬请期待。

 

 

本文翻译文档的原英文文档位于linux内核源码树

/Documentation/kbuild目录下



Linux内核的Makefiles

 

这个文档描述Linux内核的Makefiles

 

=== 目录

 

         ===1 概要

         ===2 Makefiles对于不同角色的作用

         ===3 kbuild文件

            --- 3.1 目标定义

            --- 3.2 Built-in对象目标 - 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 具体架构的Makefiles

            --- 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 下一步的工作

 

=== 1 概要

 

Makefiles包含下面5个部分:

 

         Makefile                                        顶层Makefile文件.

         .config                                            内核配置文件

         arch/$(ARCH)/Makefile            体系架构的Makefile文件.

         scripts/Makefile.*                      对于所有kbuild和Makefiles都有效的规则等

         kbuildMakefiles                          大约有500个makefiles

 

顶层Makefiles读取内核配置程序生成的.config文件。

 

顶层Makefile用来生成两个主要的目标:vmlinux(内核映像)和模块(任何模块文件)。

Makefile通过递归遍历内核树中的所有子目录来生成这些目标。

递归访问的子目录列表时依靠内核配置文件来决定的。顶层Makefile包含一个名叫arch/$(ARCH)/Makefile的体系架构Makefile。这个Makefile文件向顶层Makefile提供具体的架构信息。

 

每个子目录都有一个kbuild Makefile,这个Makefile承载着从上层Makefile传递下来的命令。kbuild Makefile使用.config文件中的信息来生成文件列表,kbuild使用这个文件列表来决定生成需要加载入内核的目标或者模块。

 

scripts/Makefile.*包含了所有的定义、规则等等。基于kbuild makefiles使用这些定义、规则可以构建内核。

 

 

=== 2 Makefiles对于不同角色的作用

 

有四类不同的人会用到Makefiles。

 

*用户*是生成内核的人。这类人通常使用“make menuconfig”或者“make”命令。他们通常不去阅读或者修改makefile文件(或者其它任何源文件)。

 

*一般的开发者*是工作在内核外层的人,比如设备驱动,文件系统以及网络协议。这些人需要去维护他们修改的子系统的kbuild Makefiles。为了有效地去实现这些工作,他们通常取药一些关于内核makefile的整体知识,加上一些关于kuild公共接口的具体信息。

 

*架构开发者*是那些工作对象是整个体系架构的人,比如sparc或者ia64。架构开发者需要知道架构Makefile,同时也需要了解kbuild Makefiles。

 

*Kbuild开发者*这类人的工作对象是整个内核系统本身。他们需要知道内核Makefiles的方方面面。

 

这个文档的适合读者是一般开发者和架构开发者。

 

 

=== 3  kbuild文件

 

内核中的大多数Makefiles是使用kbuild基本语法规则的kbuild Makefiles。这一章介绍kbuild makefiles的语法。

kbuild文件的首选名字是'Makefile',但是'Kbuild'也可以使用。如果'Makefile'和'Kbuild' 文件同时存在,那么'Kbuild'文件将会被使用。

 

3.1节“目标定义”只是一个快速入门,更多的内容请见后面的章节。

 

--- 3.1 目标定义

          

              目标定义是kbuildMakefile文件的主要部分(核心),它们定义了将要被构建的文件,任何具体的编译选项,以及任何将要递归进入的子目录。

 

      最简单的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

     

      KbuildMakefile在$(obj-y)列表中指定了编译进vmlinux的对象文件。这个列表依靠内核配置文件生成。

 

      Kbuild编译所有的$(obj-y)文件。然后调用"$(LD)-r"将这些编译生成的文件合成到一个built-in.o文件中。Built-in.o稍后会被父层Makfile链接到vmlinux中。

 

      $(obj-y)列表中文件的顺序是有意义的。文件可以在$(obj-y)列表中重复,但是只有第一个出现的文件会被编译到built-in.o中,而接下来出现的重复文件会被忽略。

 

      链接顺序也是有意义的。因为某些函数(比如(module_init() / __initcall))在系统启动的过程中将会按照它们出现的顺序调用。所以记住改变链接顺序将会造成一些不确定情况。比如改变你的SCSI控制器的的被检测顺序,那么你的磁盘就会被重新编号。

 

       例子:

                      #drivers/isdn/i4l/Makefile

                    # Makefile for the kernel ISDN subsystemand device drivers.

                      # Each configuration option enables a listof 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)变量来让Kbuild知道。

 

                例子:

                       #drivers/isdn/i4l/Makefile

                       obj-$(CONFIG_ISDN) += isdn.o

                       isdn-objs := isdn_net_lib.o isdn_v110.oisdn_common.o         

 

                在这个例子中,模块的名字是isdn.o。Kbuild将会编译生成$(isdn-objs)变量中列出的目标文件。然后运行"$(LD) -r"命令来链接列表中的文件从而生成isdn.o。

 

                Kbuild通过后缀名-objs和-y来判断一个对象是否是一个复合对象。这允许Makefiles使用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 导出符号的对象

 

      在makefiles中,模块不需要任何特殊的表示就可以导出符号。

 

--- 3.5库文件目标 - lib-y

 

      使用obj-*为前缀列出的对象是指定目录中将要编译生成的模块或者编译进built-in.o的目标。但是也有一些列出的目标有可能会包含到库文件lib.a中去。

                以lib-y开头列出的对象将会被包含到对应目录下的一个单独的库文件中。

                在obj-y列表中的对象以及附加在lib-y列表中的对象将不会被包含到库文件中,因为它们可以以任何方式读取。

                为了保持一致性,lib-m列表中的对象将被包含进lib.a。

 

                需要注意的是同一个kbuildmakefile 可能会列出编译进内核的文件以及包含进库中的文件。因此同一个目录可能会包含一个built-in.o和一个lib.a文件。

 

例子:

            #arch/i386/lib/Makefile

            lib-y    := checksum.o delay.o

 

这个例子将会基于checksum.o和delay.o生成一个库文件lib.a。只有将目录置于列      

表libs-y中,kbuild才会知道该目录下有一个lib.a要生成。

其它详情请看“6.3递归时列出需要访问的目录”。

 

一般只有lib/和arch/*/lib目录下可以使用lib-y

 

 

--- 3.6 进入子目录

     

                一个Makefile文件只负责编译它所处目录的文件。子目录中的文件应该由子目录中的Makefile来进行维护。编译系统会自动递归进入子目录,从而提供一种让编译系统知道子目录存在的机制。

 

                为了达到这个目的,将使用obj-y和obj-m。

                ext2在一个单独的文件中,fs/目录中的Makefile只用如下的表达式来告诉kbuild来进入子目录。

 

                例子:

                            #fs/Makefile

                       obj-$(CONFIG_EXT2_FS) += ext2/

 

                如果obj-中相应的CONFIG_EXT2_FS变量被设置成‘y’或者‘m’,那么kbuild将会进入到ext2目录中。

                Kbuild只能利用这些信息来决定是否需要进入一个子目录。子目录中的Makefile具体指定哪些目标会是模块,而哪些会是编译进内核的。

 

                使用CONFIG_变量给目录名赋值是一个非常好的办法。当CONFIG_选项既不是‘y’也不是‘m’的时候,kbuild就能完全忽略该目录。

 

--- 3.7 编译标记

 

  ccflags-y,asflags-y 和 ldflags-y

                这三个标记只适用于分配它们的kbuild makefile。它们一般在递归生成过程中被正常的cc,as和ld调用。

                注意:之前命名的具有相同行为的标记为:EXTRA_CFLAGS, EXTRA_AFLAGS and EXTRA_LDFLAGS.这些标记虽然现在仍然支持,但是已经被弃用了。

 

                ccflags-y指定了用$(CC)编译C文件时的选项。

 

                例子:

                            #drivers/sound/emu10k1/Makefile

                            ccflags-y += -I$(obj)

                           ccflags-$(DEBUG)+= -DEMU10K1_DEBUG

               

                这个变量时必须的。因为顶层Makefile具有$(KBUILD_CFLAGS)变量,并且将该变量作为整棵内核树的编译选项。

 

                asflags-y是一个类似于ccflags-y的字符串,它是编译汇编文件的过程中一个选项。

 

                例子:

                            #arch/x86_64/kernel/Makefile

                            asflags-y := -traditional

 

                在任何目录中,ldflags-y是$(LD)的一个选项。

 

                例子:

                            #arch/m68k/fpsp040/Makefile

                           ldflags-y:= -x

 

CFLAGS_$@, AFLAGS_$@

                CFLAGS_$@,和AFLAGS_$@只对于当前kbuildmakefile文件中的命令有效。

 

                对于每个文件,$(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,and 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) 所有的先决文件(both*.c and *.h)

                2) 在所有先决文件中使用的CONFIG_选项

                3) 编译目标中使用的命令行

 

                因此,如果你改变了$(CC)的某个选项,那么所有相关的文件将会被重新编译。

 

--- 3.10 特殊规则

 

                当kbuild机制没有提供必要的支持时,特殊规则就起作用了。一个典型的例子是在生成过程中生成的头文件。

                另一个例子是与具体架构相关的Makefiles需要特殊规则来准备启动映像。

 

                特殊规则的书写和正常的Make规则一样。

                Kbuild不在Makefile所在的目录中执行,所以所有的特殊规则需要提供相对路径给先决文件和目标文件。

 

                有两个用来定义特殊规则:

  $(src)

                $(src)是一个用来指向makfile所在目录的相对路径。必须使用$(src) 变量来引用src源码树中的文件。

 

  $(obj)

                $(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编译器,但是其它的编译器也是可以的。

 

  as-option

                as-option用来检查$(CC)是否 – 当用来编译汇编(*.S)文件 – 支持给定的选项。当不支持第一个选项的时候,第二个可选选项可能会被指定。

 

                例子:

                            #arch/sh/Makefile

                            cflags-y += $(callas-option,-Wa$(comma)-isa=$(isa-y),)

 

                在上面的例子中,当支持$(CC)的时候,cflags-y将等于选项-Wa$(comma)-isa=$(isa-y)的值。第二个参数是可选的,当不支持第一个选项,并且第二个选项有给出的时候,它就会被使用。

 

  ld-option

                ld-option用来在链接目标文件时检查是否支持给定的选项。当不支持第一个选项的时候,一个可选的第二选项可能会被支持。

 

                例子:

                            #arch/i386/kernel/Makefile

                            vsyscall-flags += $(call ld-option,-Wl$(comma)--hash-style=sysv)

 

                在上面的例子中,如果支持$(CC),那么vsyscall-flags就会被赋予-Wl$(comma)--hash-style=sysv选项的值。第二个参数是可选的,当不支持第一个选项,并且第二个选项有给出的时候,它就会被使用。

 

  as-instr

                as-instr用来检查汇编器是否报告了一个特定的指令,然后输入选项1或者选项2。C测试指令支持C转义。

                注意:as-instr-option使用KBUILD_AFLAGS作为$(AS)的选项。

 

  cc-option

                cc-option用来检查$(CC)是否支持一个给定的选项,而不是支持使用一个可选的第二选项。

 

                例子:

                            #arch/i386/Makefile

                            cflags-y += $(callcc-option,-march=pentium-mmx,-march=i586)

 

                在上面的例子中,如果支持$(CC),那么cflags-y将会被赋予-march=pentium-mmx选项的值,否则就赋予-march=i586选项的值。对于cc-option,第二个参数是可选的。如果第二参数被省略了并且第一个选项不被支持,那么cflags-y将会被置为空。

                注意:cc-option使用KBUILD_CFLAGS作为$(CC)的选项。

 

  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-yn使用KBUILD_CFLAGS作为$(CC)的选项。

 

  cc-option-align

                版本号大于3.0的gcc为了指定函数以及循环的对齐方式,改变了选项的类型。当为对齐选项使用前缀时,$(cc-option-align)将选择合适的前缀:

                gcc< 3.00

                   cc-option-align= -malign

                gcc>= 3.00

                   cc-option-align= -falign  

 

                例子:

                            KBUILD_CFLAGS+= $(cc-option-align)-functions=4

               

                在上面的例子中,选项-falign-functions=4用于gcc >=3.00的情况。对于gcc < 3.00的情况,使用 -malign-functions=4 选项.

                注意:cc-option-align使用KBUILD_CFLAGS作为$(CC) 的选项

 

  cc-version

                cc-version返回一个用数字表示的$(CC)编译器版本。数字版本的格式是<major><minor>,这两部分都是数字。比如gcc 3.41将会返回0341.

                当一个特定的$(CC)版本在某个地方是错误的,cc-version就会非常有用。比如尽管cc-version选项会被gcc给接受,但是-mregparm=3在某些gcc版本中还是不会成立。

 

                例子:

                            #arch/i386/Makefile

                            cflags-y += $(shell \

                            if [ $(call cc-version) -ge 0300 ] ;then \

                                     echo "-mregparm=3"; fi ;)

 

                在上面的例子中,-mregparm=3只有当gcc版本大于等于3.0的时候才成立。

 

  cc-ifversion

                cc-ifversion用来测试$(CC)的版本,并且当版本表达式的值为真的时候,它的值等于最后一个参数的值。

 

                例子:

                            #fs/reiserfs/Makefile

                            ccflags-y := $(call cc-ifversion, -lt,0402, -O1)

 

                在这个例子中,当$(CC)的版本号小于4.2的时候,ccflags-y的值将会等于-01的值。

                cc-ifversion语序所有的shell操作符:

                -eq,-ne, -lt, -le, -gt, 和-ge

                在这个例子中,第三个参数可以是一个文本,但是它也可以是一个扩展变量或者宏。

 

  cc-fullversion

                cc-fullversion用于需要gcc完整版本号的时候。一个典型的应用是当一个具体GCC版本号被损坏的情况。cc-fullversion比cc-version提供了一个更加具体的版本号。

 

                例子:

                            #arch/powerpc/Makefile

                           $(Q)iftest "$(call cc-fullversion)" = "040200" ; then \

                            echo-n '*** GCC-4.2.0 cannot compile the 64-bit powerpc ' ; \

                            false; \

                           fi

                在这个例子中,对于某一个具体的GCC版本,build会提示用户错误。

 

   cc-cross-prefix

                cc-cross-prefix用来检查包含$(CC)的路径中是否存在列表前缀中的某一个。$(CC)路径中存在的第一个列表中的前缀将被返回– 如果不存在列表中的前缀,那么就返回空。

                在调用cc-cross-prefix的过程中,附加的前缀使用单个空格来分开。

                这个功能用在与体系架构相关的Makefile中。当试图给CROSS_COMPILE赋值但是又存在多个可以选择的值时,这个功能就能发挥作用了。

                建议只有在交叉编译时(主机架构与目标架构不一样)才设置CROSS_COMPILE的值。如果CROSS_COMPILE的值已经被设置,那么就去除它原来的值。

 

                例子:

                            #arch/m68k/Makefile

                            ifneq ($(SUBARCH),$(ARCH))

                           ifeq ($(CROSS_COMPILE),)

                                  CROSS_COMPILE := $(callcc-cross-prefix, m68k-linux-gnu-)

                                     endif

                           endif

 

 

=== 4 主机程序支持

 

Kbuild支持在编译阶段使用主机上的可执行文件。

为了使用主机可执行文件需要两个步骤。

 

第一步是告诉kbuild存在一个主机程序。这可以通过使用hostprogs-y变量来实现。

第二步是给可执行文件增加一个明确地依赖项。这可以通过两种方法实现。通过在规则中增

加依赖项或者使用$(always)变量。

这两种可能性都在下面描述。

 

--- 4.1 一个简单的主机程序例子

               

                在某些情况下,需要在build运行的主机上编译和运行一个程序。

                下面的几行将告诉kbuild有一个程序bin2hex将要在build运行的主机上编译运行。

 

                例子:

                            hostprogs-y:= bin2hex

 

                kbuild假设上面例子中的bin2hex是从一个名叫bin2hex.c的c源文件编译生成的,它与Makefile处于同一个目录下面。

 

--- 4.2 复合主机程序

 

                主机程序可以基于复合对象生成。

                用来给主机程序定义复合对象的语法与定义内核对象的语法类似。

                $(<executable>-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提供了对共享库的支持,但是对共享库的使用应该严格限制。

                在下面的例子中,共享库libkconfig.so用来连接可执行文件conf。

               

                例子:

                            #scripts/kconfig/Makefile

                            hostprogs-y     := conf

                            conf-objs       := conf.o libkconfig.so

                            libkconfig-objs := expr.o type.o

 

                在makefile文件中,共享库需要相应的-obs行,在上面的例子中,共享库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

 

                在上面的例子中可执行文件由名叫qconf.cc的C++文件组成– 使用$(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_piggyback.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)/gen-devlist更新后,目标$(obj)/devlist.h才会被编译。注意特殊规则中对主机程序的引用必须使用前缀$(obj)。

 

                (2)使用$(always)变量

                当没有合适的特殊规则,并且在kbuild进入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的清除机制

 

“make clean”命令删除大多数内核编译目录的目标树中生成的文件。要删除的文件包括所

编译过程中生成的文件,比如主机程序。Kbuild知道(hostprogs-y), $(hostprogs-m), $(always),

$(extra-y) 和$(targets)这些列表中存在的目标,这些目标都会在执行"make clean"命令后删除。

匹配"*.[oas]", "*.ko"模式的文件加上一些由kbuild生成的附加文件,在"makeclean"执行时都

会从内核源码树中删除。

 

在kbuild makefiles中可以使用(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在同一目录下。

 

如果makefile中存在类似"obj-* := dir/"的语句,那么kbuild就必须进入到指定的子目录中去,

但是在于体系架构相关的makefiles中kbuild的机制不够全面,这种情况需要明确指出来。

 

                例子:

                            #arch/i386/boot/Makefile

                           subdir-:= compressed/

 

上面的等式告诉kbuild在执行"make clean"命令时,同时也需要进入子目录compressed/中执

行该命令。

 

存在一个叫做archclean的可选目标,用来支持生成最终启动映像的Makefiles的清除机制。

 

                例子:

                            #arch/i386/Makefile

                            archclean:

                                     $(Q)$(MAKE) $(clean)=arch/i386/boot

 

当执行"make clean"命令时,make会进入arch/i386/boot目录,并且像一般情况一样进行清

除。arch/i386/boot目录中的Makefile可以使用subdir-来跟踪到更深层次的目录中去。

 

注意1:arch/$(ARCH)/Makefile不能使用"subdir-",因为那个文件是包含在顶层makefile中,

而kbuild机制在那个时候还是不能工作的。

 

注意2:在执行"make clean"时,所有core-y, libs-y, drivers-y 和 net-y列表中的目录都将会被

访问。

 

 

=== 6具体架构的Makefiles

 

顶层Makefile在开始进入单个的目录之前会设置环境并且做一些准备工作。顶层makefile

中包含一些通用部分,而arch/$(ARCH)/Makefile包含为了表示架构而建立kbuild需要的东西。

arch/$(ARCH)/Makefile建立了一些变量以及定义了一些目标来做准备。

 

当kbuild执行时,将会执行下面这些步骤(不一定完全是):

1)  配置内核 => 产生 .config文件

2)  将内核版本号保存到include/linux/version.h文件中

3)  符号链接include/asm到include/asm-$(ARCH)

4)  更新目标的所有先决条件:-附加的先决条件在arch/$(ARCH)/Makefile中指定

5)  递归地进入init-*core* drivers-* net-* libs-*列表中的列出的所有目录,并且编译生成所有目标。

-         上述变量的值是在arch /$(ARCH)/ Makefile中扩充

 

6)  所有生成的目标将会被链接最后生成vmlinux文件,它位于目标树的根部。

7)  最终,特定于体系结构的那部分程序会做所有善后工作,并且生成最终的bootimage。

-         建立boot记录

-         准备initrd映像和类似的东西

 

 

 

--- 6.1 设置用来调整生成架构的变量

        

  LDFLAGS                通用 $(LD) 选项

        

         用于所有调用连接器的标志。

         经常指定仿真器就足够了。

 

         例子:

                   #arch/s390/Makefile

                   LDFLAGS         := -m elf_s390

         注意:ldflags-y可以进一步定制该标志的使用。见3.7章。

 

  LDFLAGS_MODULE        $(LD)链接模块时的选项

 

         LDFLAGS_MODULE用来指定$(LD)链接用于模块的.ko文件时的标志。

         默认情况下“-r”是用于重定位输出。

 

  LDFLAGS_vmlinux         $(LD)链接vmlinux时的选项

        

         当链接最终的vmlinux映像的时候,LDFLAGS_vmlinux指定传递给链接的附加标志。

         LDFLAGS_vmlinux使用LDFLAGS_$@支持。

 

         例子:

                   #arch/i386/Makefile

                   LDFLAGS_vmlinux:= -e stext

 

  OBJCOPYFLAGS          objcopy标志

 

         当使用$(callif_changed,objcopy)来转换一个.o文件时,OBJCOPYFLAGS中指定的标志就     会被使用。

         $(callif_changed,objcopy)经常用来生成vmlinux中的原始二进制文件。

 

         例子:

                   #arch/s390/Makefile

                   OBJCOPYFLAGS:= -O binary

 

                   #arch/s390/boot/Makefile

                   $(obj)/image:vmlinux FORCE

                            $(callif_changed,objcopy)

 

         这个例子中,二进制文件 $(obj)/image是vmlinux得一个二进制版本。$(call    if_changed,xxx)变量的使用将会在后面描述。

 

  KBUILD_AFLAGS               $(AS)  汇编器标志

 

         默认值 - 请看顶层Makfile文件

         根据系统架构的要求进行追加或者修改。

 

         例子:

                   #arch/sparc64/Makefile

                   KBUILD_AFLAGS+= -m64 -mcpu=ultrasparc

 

  KBUILD_CFLAGS                $(CC)  编译器标志

 

         默认值 - 请看顶层Makfile文件

         根据系统架构的要求进行追加或者修改。

 

         大多数情况下,KBUILD_CFLAGS变量值依赖于配置程序

 

         例子:

                   #arch/i386/Makefile

                   cflags-$(CONFIG_M386)+= -march=i386

                   KBUILD_CFLAGS+= $(cflags-y)

 

         许多体系相关的Makefiles会动态运行目标C编译器来探测支持的选项:

                  

                   #arch/i386/Makefile

 

                   ...

                   cflags-$(CONFIG_MPENTIUMII)     += $(call cc-option,\

                                                        -march=pentium2,-march=i686)

                   ...

                   #Disable unit-at-a-time mode ...

                   KBUILD_CFLAGS+= $(call cc-option,-fno-unit-at-a-time)

                   ...

 

         第一个例子中使用了一个技巧,当一个配置选项被选择时,它的值会被扩展到‘y’。

 

  CFLAGS_KERNEL                 $(CC)  为编译进内核指定的选项

 

         $(CFLAGS_KERNEL)包含额外的C编译器标志用来编译常驻内存的代码。

 

  CFLAGS_MODULE                $(CC)  为模块指定的选项

 

       $(CFLAGS_MODULE)包含额外的C编译器标志用来编译可加载内核模块的代码。

 

 

--- 6.2 增加先决条件到archprepare

 

         archprepare:这是一条用来列出先决条件的规则,这些先决条件是build进入子目录前 必须建立的。

这将从在包含汇编内容的头文件中使用。

 

                   例子:

                            #arch/arm/Makefile

                            archprepare: maketools

 

在这个例子中,在进入到子目录前目标文件maketools将会被处理。

想要知道kbuild怎么支持生成偏移头文件,请看XXX-TODO这一章。

 

--- 6.3 递归时列出需要访问的目录

 

         一个体系相关的Makefile结合顶层Makefile定义了用来指定怎么编译vmlinux文件的变

         量。注意没有给模块指定体系相关的区域;模块生成机制全是体系无关的。

 

 

    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) and $(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/中指定的目标。因此应该手动调用         make arch/$(ARCH)/Makefile来生成boot/中的目标。

        

         一种比较提倡的方法是在arch/$(ARCH)/Makefile中包含一个快捷方式,当进入到     arch/$(ARCH)/boot/Makefile文件中时再使用完整的路径。

 

         例子:

                   #arch/i386/Makefile

                   boot:= arch/i386/boot

                   bzImage:vmlinux

                            $(Q)$(MAKE)$(build)=$(boot) $(boot)/$@

 

         "$(Q)$(MAKE)$(build)=<dir>"是一种提倡的方法来激活子目录中的make。

 

         没有任何规则用来命名体系相关的目标,但是执行"make help"命令将会列出所有相关的         目标。为了支持这个命令,必须先定义$(archhelp)。

 

         例子:

                   #arch/i386/Makefile

                   definearchhelp

                     echo '* bzImage      - Image(arch/$(ARCH)/boot/bzImage)'

                   endif

 

         当不带任何参数执行make命令的时候,将会生成第一个遇到的目标。当前顶层Makefile        文件的第一个目标是all:。

         默认情况下,每一个架构应该生成一个可引导映像。在"make help"中,默认目标使用一         个'*'来突出显示。向all:中添加一个新的先决条件:选择一个与vmlinux不同的默认目  标。

 

         例子:

                   #arch/i386/Makefile

                   all:bzImage

 

         当不带任何参数执行“make”命令时,bzImage将会被建立生成。

 

--- 6.5 建立非kbuild的目标

 

  extra-y

        

         extra-y指定在当前目录创建的额外目标,除了obj-*中指定的任何目标。

 

         在下面两种情况下,将所有目标放在extra-y列表下是必要的:

1)  使能kbuild来检查命令行中的改变 – 当 $(callif_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是下面命令行的基础。

 

         Usage:

                   target:source(s) FORCE

                            $(callif_changed,ld/objcopy/gzip)

 

         当一个规则被求值的时候,会检查规则相对于上次被调用是否有文件被更新或者命令行         被改变。如果任何可执行文件的选项被改变,那么就会强制执行rebuild。

         如果任何目标使用了if_changed,那么它必须放进$(targets)列表中,不然命令行的检查        会失败,并且目标会一直被建立。

         $(targets)的等式中没有使用 $(obj)/前缀。

         if_changed可以结合和6.7节"Customkbuild commands"中定义的用户命令。

 

         注意:一个典型的错误就是忘记了 FORCE 先决条件。

         另外一个常见的陷阱就是空格有时很显著;比如,下面的例子将会失败(注意逗号后面         的多余空格):

                   target:source(s) FORCE

         #WRONG!#      $(call if_changed, ld/objcopy/gzip)

 

  ld

         链接目标。LDFLAGS_$@经常用来指定ld的选项。

 

 objcopy

         复制二进制。OBJCOPYFLAGS的使用经常在arch/$(ARCH)/Makefile文件中指定。

         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 bootsect bootsect.o

                   $(obj)/setup$(obj)/bootsect: %: %.o FORCE

                            $(callif_changed,ld)

 

         在这个例子中,有两个可能的目标,在它们在链接的时候需要不同的选项。通过使用     LDFLAGS_$@语法格式来指定链接选项 -每一个可能的目标都需要指定。

         $(targets)代表的值是所有可能的目标,kbuild通过$(targets)来知道这些目标,然后执行 下面的操作:

                   1)检查命令行的改变

       2)执行make clean时删除目标

 

         先决条件中的":%: %.o" 部分是一个简写方式,它可以让我们不用列出setup.o和    bootsect.o文件。

         注意:一个常见的错误就是忘记"target :="赋值表达式,导致目标文件在没有明显原因   的情况下被重新编译。

 

--- 6.7 自定义kbuild命令

 

         当kbuild运行时KBUILD_VERBOSE=0,那么只正常显示一个命令的简写方式。

         为了使能自定义命令这一行为,kbuild需要设置两个变量:

         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

                            $(callif_changed,image)

                            @echo'Kernel: $@ is ready'

 

         当更新 $(obj)/bzImage的时候,这一行

        

         BUILD    arch/i386/boot/bzImage

 

         将会显示为 "makeKBUILD_VERBOSE=0"。

 

--- 6.8 预处理链接脚本

 

         当生成vmlinux映像的时候,会使用连接器脚本arch/$(ARCH)/kernel/vmlinux。

         这个脚本是vmlinux.lds.S文件的一个预处理变种,vmlinux.lds.S文件与这个脚本在同一    个目录下。

         Kbuild知道.lds文件,并且包含一条*lds.S-> *lds规则。

 

         例子:

                   #arch/i386/kernel/Makefile

                   always:= vmlinux.lds

 

                   #Makefile

                   exportCPPFLAGS_vmlinux.lds += -P -C -U$(ARCH)

 

         赋给$(always)变量的等式是用来告诉kbuild建立vmlinux.lds目标。

         赋给$(CPPFLAGS_vmlinux.lds)变量的等式是用来告诉kbuild在建立vmlinux.lds目标的时     候使用指定的选项。

 

         当建立*.lds目标的时候,kbuild使用下面的这些变量:

         KBUILD_CPPFLAGS  : 在顶层Makefile文件中设置

         cppflags-y         : 可能在kbuild makefile文件中设置

         CPPFLAGS_$(@F)  : 针对特定的标志.

                           注意在这个赋值表达式中使用文件全名。

 

         针对*lds文件的kbuild机制在一些体系相关的文件中使用。

 

 

=== 7 Kbuild 变量

 

顶层Makefile导出了下列的一些变量:

 

         VERSION,PATCHLEVEL, SUBLEVEL, EXTRAVERSIO

                  

                   这些变量定义了当前内核的版本。很少有架构的Makefiles直接使用这些变量值;                      它们应该使用$(KERNELRELEASE)变量值。

                  

                   $(VERSION),$(PATCHLEVEL), and $(SUBLEVEL) 定义了版本号基本的三个部分,比如                      "2","4", 和 "0"。这三个部分只能是数字。

 

                   $(EXTRAVERSION)定义了一个更小的版本子级别,它一般用于还不是正式的补丁以                      及额外的补丁。它经常是一个非数字的字符串,比如"-pre4",并且它大多数情况下                           是空的。

 

         KERNELRELEASE

                  

                   $(KERNELRELEASE)是一个单个的字符串,比如"2.4.0-pre4",它适合作为安装目录名          或者在版本号中出现。一些架构的Makefiles就基于这些目的使用了它。

 

         ARCH

 

                   这个变量在目标夹头中定义,比如”i386”,”arm”,或者”sparc”。一些kbuild makefiles                通过检查$(ARCH)变量来决定编译哪些文件。

 

                   默认情况下,顶层Makefile文件将$(ARCH)的值设置为与主机系统架构相等。在交                      叉编译的时候,用户可以通过下面的命令行来改写$(ARCH)变量的值:

 

                            makeARCH=m68k ...

 

         INSTALL_PATH

 

                   这个变量定义了一个架构Makefiles安装固定内核映像和System.map文件的地方。

             体系相关的安装目标可以使用这个目标。

 

         INSTALL_MOD_PATH,MODLIB

 

                   为了进行模块安装,$(INSTALL_MOD_PATH)为$(MODLIB)指定了一个前缀。这个变量                   没有在Makefile中定义,但是用户愿意的话可以将它传入Makefile中。

 

                   $(MODLIB)指定了模块安装的目录。

                   顶层Makefile为$(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)定义了                         $(MODLIB)变量。用户如果愿意的话可以通过命令行来改写这个值。

 

         INSTALL_MOD_STRIP

 

                   如果这个变量被指定了,那么将导致模块在安装之后被去除。如果                                          INSTALL_MOD_STRIP的值为‘1’,那么会使用默认的选项--strip-debug。否则,                      INSTALL_MOD_STRIP将会被作为strip命令的选项。

 

 

=== 8 Makefile 语言

 

内核 Makefiles被设计成使用GNU Make运行。Makefiles只使用GNU Make定义的功能,但是它们使用了许多GNU的扩展功能。

 

GNU Make支持基本的list-processing功能。内核Makefiles使用一些样式新颖的列表来建立和操作一些"if"语句。

 

GNU Make有两个赋值操作符,":="和"=" 。":="立即计算出等式右边的值,并且储存一个实际的字符串到等式左边。"="就像一个规则定义;它将等式右边直接储存到等式左边而不计算出实际的值,当每次使用等式左边的值时再去计算出实际的值。

 

有一些情况使用"="是合适的。但是大多数情况下使用":="是合适的选择。

 

 

=== 9贡献者

 

原始的版本由Michael Elizabeth Chastain 提供,<发送邮件至 : mec@shout.net>

Kai Germaschewski进行了更新 kai@tp1.ruhr-uni-bochum.de

Sam Ravnborg 进行了更新 sam@ravnborg.org

文本质量保证由Jan Engelhardt 担任jengelh@gmx.de

 

=== 10 下一步的工作

 

- 描述kbuild怎么使用_shipped来支持shipped文件

- 生成offset头文件

- 增加更多的变量到 section 7?


  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值