Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1640160
  • 博文数量: 126
  • 博客积分: 1541
  • 博客等级: 上尉
  • 技术积分: 1914
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-20 11:14
文章分类

全部博文(126)

文章存档

2014年(21)

2013年(42)

2012年(63)

分类: LINUX

2012-12-01 14:36:16

Linux Kernel Makefiles

本文由王立于 2003 年 5 月 3 日翻译。原文为 linux-2.4.20 中 Documentation/kbuild/makefiles.txt,由 Michael Elizabeth Chastain 于 2000 年 9 月 14 日作。

1、

Makefile 由五个部分组成:

  • Makefile:顶层 Makefile。
  • .config:内核配置文件。
  • arch/*/Makefile:体系结构 Makefiles。
  • 子目录 Makefile:大约三百个。
  • Rules.make:为所有子目录 Makefile 提供通用规则。

顶层 Makefile 读入在内核配置过程中生成的 .config 文件。

顶层 Makefile 负责两个主要产品的创建:vminux (常驻内核映象) 和模块 (任何模块文件)。它通过递归下降到内核源代码树以创建这些目标。需要进入的子目录由内核配置确定。

顶层 Makefile 引入一个名为 arch/$(ARCH)/Makefile 的体系结构 Makefile。体系结构 Makefile 为顶层 Makefile 提供体系结构特定的信息。

每一子目录都有一个 Makefile 以完成从上层传递来的命令。子目录 Makefile 使用来自 .config 文件的信息以构造各种文件列表,而后引入 Rules.make 中的通用规则。

Rules.make 定义了所有子目录 Makefile 的通用规则。它的一个变量列表组成了它的公共界面。而后它根据这些列表声明规则。

2、

人们跟内核的 Makefile 有四种不同的关系。

用户是创建内核的人。这些人输入诸如“make menuconfig”或 “make bzImage”之类的命令。他们通常并不阅读或编辑内核 Makefile (或任何其它源代码)。

普通开发者是开发诸如设备驱动程序、文件系统或网络协议的人。这些人需要为他们开发的子系统维护用于该子系统的子目录 Makefile。为了有效地完成这一维护任务,他们需要一些关于内核 Makefile 的全局性的知识,以及一些关于 Rules.make 公共界面的细节。

体系结构开发者是开发整个体系结构 (例如 spare 或 ia64) 的人。体系结构开发者需要理解体系结构 Makefile 以及子目录 Makefile。

Kbuild 开发者是开发内核创建系统本身的人。这些人需要知道内核 Makefile 的所有方面。

本文档是面向普通开发者和体系结构开发者的。

3、

内核 Makefile 被设计为使用 GNU Make。这些 Makefiles 只使用 GNU Make 文档说明了的功能,但它们使用了很多 GNU 扩展。

GNU Make 支持基本列表处理函数。内核 Makefile 使用带有少量 if 语句的新颖的列表创建和操作风格。

GNU Make 有两种赋值操作符:“:=”和“。“: ”立即对右侧进行求值并且将结果字符串存储于左侧变量中。“=”更像公式定义;它以未求值形式保存右侧的内容并在使用左侧变量的时候对此内容进行求值。

在有些情况下“=”比较适用。但通常“:=”是正确的选择。

本文档的所有示例都来自于实际的内核源代码。这些示例都被重新排版过 (改变了空白符和行的划分),但其它都完全一致。

4、

顶层 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”。许多子目录 Makefile 测试 $(ARCH) 以决定编译那些文件。
在默认情况下,顶层 Makefile 将 $(ARCH) 设定为主机系统的体系机构。为了进行交叉创建,用户可以在命令行覆盖 $(ARCH) 的值。
make ARCH=m68k ...
TOPDIR、HPATH
$(TOPDIR) 是内核源代码树顶层目录的路径。子目录 Makefile 需要此变量以便引入 $(TOPDIR)/Rules.make。
$(HPATH) 等价于 $(TOPDIR)/include。少数体系结构 Makefile 需要它以使用引入文件做一些特殊的事。
SUBDIRS
$(SUBDIRS) 是顶层 Makefile 应该进入以便完成 vmlinux 或模块创建的目录列表。$(SUBDIRS) 含有那些目录取决于内核配置。顶层 Makefile 定义此变量,体系结构Makefile 扩展此变量。
HEAD、CORE_FILES、NETWORKS、DRIVERS、LIBS、LINKFLAGS
$(HEAD)、$(CORE_FILES)、$(NETWORKS)、$(DRIVERS) 和 $(LIBS) 给出要连接到 vmlinux 中的目标文件和库的列表。
$(HEAD) 中的文件首先连接到 vmlinux 中。
$(LINKFLAGS) 指定创建 vmlinux 的标志。
顶层 Makefile 和体系结构 Makefile 联合定义这些变量。顶层 Makefile 定义 $(CORE_FILES)、$(NETWORKS)、$(DRIVERS) 和 $(LIBS)。体系结构 Makefile 定义 $(HEAD) 和 $(LINKFLAGS) 并扩展 $(CORE_FILES) 和 $(LIBS)。
注意:这些变量并不都是必需的。$(NETWORKS)、$(DRIVERS)甚至 $(LIBS) 都应该合并到 $(CORE_FILES) 中去。
CPP、CC、AS、LD、AR、NM、STRIP、OBJCOPY、OBJDUMP、CPPFLAGS、CFLAGS、CFLAGS_KERNEL、MODFLAGS、AFLAGS、LDFLAGS、PERL、GENKSYMS
这些变量指定了 Rules.make 用于从源代码文件创建目标文件的命令和标志。
$(CFLAGS_KERNEL) 含有用于编译常驻内核代码的附加 C 编译器标志。
$(MODFLAGS) 含有用于编译可载入内核模块的附加 C 编译器标志。将来该标志可能要改为更通用的 $(CFLAGS_MODULE)。
$(AFLAGS) 含有汇编标志。
$(GENKSYMS) 含有用于生成在启用了 CONFIG_MODVERSIONS 时内核符号签名的命令。 genksyms 命令由 modutils 包提供。
CROSS_COMPILE
该变量是诸如 $(CC)、$(AS) 和 $(LD) 之类的其它变量的前缀路径。体系结构Makefile 有时显式使用并设置该变量。子目录 Makefile 不需要关心它。
如果需要,用户可以在命令行中覆盖 $(CROSS_COMPILE) 的值。
HOSTCC、HOSTCFLAGS
这些变量定义了用于编译在本地主机运行的程序的 C 编译器和 C 编译器标志。它们使用单独的变量是因为目标体系结构可能于主机体系结构不同。 
如果您的 Makefile 编译并运行一个在创建内核的过程中运行的程序,那它就应该使用 $(HOSTCC) 和 $(HOSTCFLAGS)。 
例如,drivers/pci 子目录含有名为 gen-devlist.c 的助手程序。该程序读入一个 PCI ID 列表并生成名为 classlist.h 和 devlist.h 的 C 代码。 
假定用户有一台 i386 计算机并需要创建一个用于 ia64 机器的内核。那么用户就应该在大多数编译中使用 ia64 交叉编译器,但应该使用 i386 本地编译器编译 drivers/pci/gen-devlist.c。 
还有,诸如 scripts/mkdep.c 和 scripts/lxdialog/*.c 那样的 kbuild 助手程序应该用 $(HOSTCC) 而不是 $(CC) 编译。
ROOT_DEV、SVGA_MODE、RAMDISK
最终用户编辑该变量以指定关于他们的内核的特定配置信息。这些变量是古董!他们也只能用于 i386 体系结构。他们实在是应该用 CONFIG_* 选项来替代了。
MAKEBOOT
该变量只在顶层 Makefile 中定义和使用。顶层 Makefile 不该导出它。
INSTALL_PATH
该变量定义了体系结构 Makefile 应该将常驻内核映象和 System.map 文件安装到那里去。
INSTALL_MOD_PATH、MODLIB
$(INSTALL_MOD_PATH) 指定了用于安装模块的 $(MODLIB) 的前缀。 Makefile 没有定义该变量,但如果需要的话,用户可以定义它。 
$(MODLIB) 指定了模块安装的目录。顶层 Makefile 将 $(MODLIB) 定义为 $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)。如果需要用户可以在命令行中覆盖这个值。
CONFIG_SHELL
该变量是 Makefile 和 Rules.make 间私有的变量。体系结构 Makefile 和子目录 Makefile 不应该使用它。
MODVERFILE
内部变量。由于它从不在顶层 Makefile 以外使用,所以不必导出它。
MAKE、MAKEFILES
一些 GNU Make 的内部变量。 
$(MAKEFILES) 特别用于强制体系结构 Makefile 和子目录 Makefile 读入 $(TOPDIR)/.config 而不必显式地引入它。(这是个实线细节并可以修正)。
5、5.1、

顶层 Makefile 引入一个体系结构 Makefile:arch/$(ARCH)/Makefile。本节解说体系结构 Makefile 的功能。

体系结构 Makefile 用体系结构特定的至扩展了某些顶层 Makefile 的变量。

SUBDIRS
顶层 Makefile 定义 $(SUBDIRS)。体系结构 Makefile 用一组体系结构特定的目录扩展 $(SUBDIRS)。 
例如:
# arch/alpha/Makefile SUBDIRS := $(SUBDIRS) arch/alpha/kernel arch/alpha/mm arch/alpha/lib arch/alpha/math-emu
该列表可能依赖于配置: # arch/arm/Makefile ifeq ($(CONFIG_ARCH_ACORN),y) SUBDIRS += drivers/acorn ... endif
CPP、CC、AS、LD、AR、NM、STRIP、OBJCOPY、OBJDUMP、CPPFLAGS、CFLAGS、CFLAGS_KERNEL、MODFLAGS、AFLAGS、LDFLAGS
顶层 Makefile 定义了这些变量,体系结构 Makefile 扩展了它们。 
很多体系结构 Makefile 动态地运行目标 C 编译器以探测它所支持的选项:
# arch/i386/Makefile # prevent gcc from keeping the stack 16 byte aligned CFLAGS += $(shell if $(CC) -mpreferred-stack-boundary=2 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mpreferred-stack-boundary=2"; fi)
而且,$(CFLAGS) 当然可能依赖于配置: # arch/i386/Makefile ifdef CONFIG_M386 CFLAGS += -march=i386 endif ifdef CONFIG_M486 CFLAGS += -march=i486 endif ifdef CONFIG_M586 CFLAGS += -march=i586 endif
某些体系结构 Makefile 重新定义了编译命令以添加体系结构特定的标志: # arch/s390/Makefile LD=$(CROSS_COMPILE)ld -m elf_s390 OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
5.2、

体系结构 Makefile 和顶层 Makefile 合作以定义确定了如何创建 vmlinux 文件的变量。请注意没有对应的体系结构特定的模块部分;模块创建机制跟体系结构是完全独立的。

HEAD、CORE_FILES、LIBS、LINKFLAGS
顶层 Makefile 定义了这些变量于体系结构独立的内容,而体系结构 Makefile 扩展了它们。请注意体系结构 Makefile 定义 (而不仅仅是扩展了) $(HEAD) 和 $(LINKFLAGS)。 
例如:
# arch/m68k/Makefile ifndef CONFIG_SUN3 LINKFLAGS = -T $(TOPDIR)/arch/m68k/vmlinux.lds else LINKFLAGS = -T $(TOPDIR)/arch/m68k/vmlinux-sun3.lds -N endif ... ifndef CONFIG_SUN3 HEAD := arch/m68k/kernel/head.o else HEAD := arch/m68k/kernel/sun3-head.o endif SUBDIRS += arch/m68k/kernel arch/m68k/mm arch/m68k/lib CORE_FILES := arch/m68k/kernel/kernel.o arch/m68k/mm/mm.o $(CORE_FILES) LIBS += arch/m68k/lib/lib.a
5.3、

体系结构 Makefile 定义了获取 vmlinux 文件、压缩它、以启动代码包装它、并将结果文件复制到某处的目标。这包括各种类型的安装命令。

这些后-vmlinux 目标在不同体系结构之间并不通用。下面是这些目标和支持它们的体系结构的列表 (来自于内核版本 2.4.0-test6-pre5):

balomips
bootimagealpha
bootpfilealpha、ia64
bzImagei386、m68k
bzdiski386
bzliloi386
compressedi386、m68k、mips、mips64、sh
dasdfmts390
Imagearm
images390
installarm、i386
lilom68k
msbalpha、ia64
my-special-bootalpha、ia64
orionbootmips
rawbootalpha
silos390
srmbootalpha
tftpboot.imgsparc、sparc64
vmlinux.64mips64
vmlinux.aoutsparc64
zImagearm, i386, m68k, mips, mips64, ppc, sh
zImage.initrdppc
zdiski386, mips, mips64, sh
zinstallarm
zliloi386
znetboot.initrdppc
5.4、

体系结构 Makefile 必须定义以下体系结构特定的目标。这些目标为对应的顶层 Makefile 目标提供了体系结构特定的工作:

archcleanclean
archdepdep
archmrpropermrproper
6、

子目录 Makefile 有四个部分。

6.1、

第一部分是注释头。历史上很多匿名人士编辑了内核 Makefile 而没有在头中留下任何修改记录;来自他们的注释将是很有价值的。

6.2、

第二部分是一组作为子目录 Makefile 核心的定义。这些行定义了需要创建的文件、所有特殊的编译选项,以及必须递归进入的子目录。这些行的声明严重地依赖于内核配置变量 (CONFIG_* 符号)。

第二部分看起来是这样:

# drivers/block/Makefile obj-$(CONFIG_MAC_FLOPPY) += swim3.o obj-$(CONFIG_BLK_DEV_FD) += floppy.o obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o 6.3、

第三部分只有一行:

include $(TOPDIR)/Rules.make 6.four、

第四部分含有任何必需而 Rules.make 中的通用规则无法完成的特殊的 Makefile 规则。

7、

Rules.make 的公共界面由以下变量组成:

7.1、

一个 Makefile 只负责它所在目录目标文件的创建。子目录中的文件应该由那些子目录中的 Makefile 来创建。只要你让创建系统知道这些子目录,创建系统就会自动调用 make 以递归地进入子目录。

为此,使用 subdir-{y,m,n,} 变量:

subdir-$(CONFIG_ISDN) += i4l subdir-$(CONFIG_ISDN_CAPI) += capi

在实际创建内核时,例如:vmlinux (“make {vmlinux、bzImage、...”),make 将递归下降到由 $(subdir-y) 列举的目录中。

当创建模块时 (“make modules”),make 将递归下降到由 $(subdir-m) 列举的目录中。

当创建依赖性关系时(“make dep”),make 需要察看所有子目录,所以它将下降到由 $(subdir-y)、$(subdir-m)、$(subdir-n)、$(subdir-) 列举的所有子目录。

您可能会遇到配置选项被设置为“y”,但您仍然需要在那个子目录中创建模块的情况。

例如,drivers/isdn/capi/Makefile 中有:

obj-$(CONFIG_ISDN_CAPI) += kernelcapi.o capiutil.o obj-$(CONFIG_ISDN_CAPI_CAPI20) += capi.o 可能 CONFIG_ISDN_CAPI=y,但 CONFIG_ISDN_CAPI_CAPI20=m。

这可以在其父母录的 Makefile 以如下形式来表述:

mod-subdirs := i4l hisax capi eicon subdir-$(CONFIG_ISDN_CAPI) += capi

即使子目录 (“capi”) 只出现在 $(subdir-y) 而不在 $(subdir-m) 中出现,使子目录 (“capi”) 出现在 $(mod-subdirs) 变量中使创建系统在 “make modules” 的过程中进入该子目录。

7.2、
O_TARGET, obj-y
子目录 Makefile 在列表 $(obj-y) 中为 vmlinux 指定目标文件。这些列表依赖于内核配置。 
Rules.make 编译所有的 $(obj-y) 文件。而后它调用 “$(LD) -r” 以将这些文件合并成为一个名为 $(O_TARGET) 的 .o 文件。这个 $(O_TARGET) 将来由父 Makefile 连接到 vmlinux 之中。 
$(obj-y) 中文件的顺序是重要的。允许列表中出现重复:第一次出现将被连接到 $(O_TARGET),而忽略随后的出现。 
连接顺序是重要的,这是因为某些函数在启动时将按照它们出现的顺序被调用。所以要记住改变连接顺序,比如说,就可能改变您探测到 SCSI 控制器的顺序,从而改变您磁盘的编号。 
例如:
# Makefile for the kernel ISDN subsystem and device drivers. # The target object and module list name. O_TARGET := vmlinux-obj.o # Each configuration option enables a list of files. obj-$(CONFIG_ISDN) += isdn.o obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o # The global Rules.make. include $(TOPDIR)/Rules.make
7.3、
L_TARGET
除了创建 O_TARGET 目标文件,您还可以再为 $(obj-y) 列举的目标文件创建静态连接库。通常不必这样做,它只用于 lib、arch/$(ARCH)/lib 目录。
7.4、
obj-m
$(obj-m) 指定了作为可载入内核模块创建的目标文件。 
一个模块可以从一个或多个源代码文件中创建出来。如果是一个源代码文件,子目录 Makefile 仅仅将文件添加到 $(obj-m) 即可。 
例如:
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
如果内核模块是从多个源代码文件中创建出来,您就以跟上面相同的方法指定您要创建的模块。 
然而,创建系统当然需要知道您要创建的模块的各个部分,所以您必须通过设定变量 $(<模块名>-obj) 来告诉它。 
例如:
obj-$(CONFIG_ISDN) += isdn.o isdn-objs := isdn_net.o isdn_tty.o isdn_v110.o isdn_common.o
在这个例子中,模块名为 isdn.o。Rules.make 将编译列举在 $(isdn-objs) 中的文件,而后对这些文件运行“$(LD) -r”以生成 isdn.o。 
注意:当然,当您将目标文件创建到内核之中时,以上语法仍然是有效的。所以,如果您的 CONFIG_ISDN=y,创建系统将按照您所预期的,由它的各个部分为您创建出 isdn.o,然后将它连接到 $(O_TARGET) 之中。
7.5、
export-objs
当使用可载入模块时,不是所有内核/其它模块中的全局符号 都自动成为可用的,您的模块只能使用那些显式导出的符号。 
为了让模块能够使用某个符号,要“导出”它,在源代码中使用 EXPORT_SYMBOL()。此外,您还要在 Makefile 变量 $(export-objs) 中列举所有导出符号的文件(例如,含有 EXPORT_SYMBOL() 指令的源代码)。 
例如:
# Objects that export symbols. export-objs := isdn_common.o
这是因为 isdn_common.c 含有 EXPORT_SYMBOL(register_isdn);
这使得底层 ISDN 驱动程序能够使用函数 register_isdn。
7.6、
EXTRA_CFLAGS、EXTRA_AFLAGS、EXTRA_LDFLAGS、EXTRA_ARFLAGS
$(EXTRA_CFLAGS) 指定用 $(C) 编译 C 文件的选项。该变量中的选项用于当前目录中编译所有文件用的 $(CC) 命令。 
例如:
# drivers/sound/emu10k1/Makefile EXTRA_CFLAGS += -I. ifdef DEBUG EXTRA_CFLAGS += -DEMU10K1_DEBUG endif
当前目录的子目录并不使用 $(EXTRA_CFLAGS)。此外,它也不用于由 $(HOSTCC) 编译的文件。 
由于顶层 Makefile 拥有变量 $(CFLAGS) 并将该变量用于整个源代码树,因此 $(EXTRA_CFLAGS) 是必需的。 
$(EXTRA_AFLAGS) 是用于编译汇编语言源代码的类似的单个目录选项字符串。 
示例:在编写本文时,内核源代码中尚无使用 $(EXTRA_AFLAGS) 的例子。 
$(EXTRA_LDFLAGS) 和 $(EXTRA_ARFLAGS) 用于 $(LD) 和 $(AR) 的类似的单个目录选项字符串。 
示例:在编写本文时,内核源代码中尚无使用 $(EXTRA_LDFLAGS) 或 $(EXTRA_ARFLAGS) 的例子。
CFLAGS_$@, AFLAGS_$@
$(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
Rules.make 有让目标文件依赖于用于编译它的 $(CFLAGS_$@) 的值的功能。因此,如果你为某个文件改变了 $(CFLAGS_$@) 的值,不论是编辑 Makefile 还是以其他某种方式覆盖了原来的值,Rules.make 都会正确地工作并以新选项重新编译您的源代码。 
请注意:由于 Rules.make 的缺陷,汇编语言没有标志依赖性。如果您为某个文件编辑了 $(AFLAGS_$@),您就必须删除目标文件以便从源文件重新创建它。
LD_RFLAG
使用但从未定义该变量。这似乎是某种已放弃的尝试的遗迹。
7.7、
IGNORE_FLAGS_OBJS
$(IGNORE_FLAGS_OBJS) 是一个无法自动跟踪标志依赖性关系的目标文件的列表。这是个补充功能,用于处理实现依赖性标志中的问题。(问题是标志依赖性假定 %.o 文件是从一个匹配的 %.S 或 %.c 中创建出来的。有时并不是这样)。
USE_STANDARD_AS_RULE
这是一个过渡变量。如果定义了 $(USE_STANDARD_AS_RULE) , Rules.make 就为将 %.S 文件汇编为 %.o 文件或 %.s 文件 (%.s 文件只对开发者才有用) 提供标准规则。 
如果没有定义 $(USE_STANDARD_AS_RULE),那么 Rules.make 就不提供这些标准规则。在这种情况下,子目录 Makefile 必须为汇编 %.S 文件提供它的私有规则。 
在过去,所有 Makefile 都提供私有的 %.S 规则。较新的 Makefiles 应该定义 USE_STANDARD_AS_RULE 并使用标准的 Rules.make 规则。一旦所有体系结构的所有 Makefile 都使用 USE_STANDARD_AS_RULE,那么 Rules.make 就可以取消对 USE_STANDARD_AS_RULE 的测试。此后,所有其它 Makefile 就可以取消对 USE_STANDARD_AS_RULE 的定义。
8、

[ 本章回溯到将前面描述的编写 Makefile 的方式称为“新风格”的时候。由于换句话说它仍然表述了同样的事,所以我在此保持了这种说法,这可能有些用处 ]

“新风格变量”比“旧风格变量”更简单更有效。因此,许多子目录 Makefile 可以缩减 60%。作者希望所有体系结构 Makefile 和子目录 Makefile 都能及时转换到新风格。

Rules.make 并不理解新风格变量。因此,每个新风格 Makefile 都有一个样板代码部分以便将新风格变量转换为旧风格变量。这有点混淆,人们以“新风格”定义大部分变量但最后几行又落入了“旧风格”。

8.1、
obj-y obj-m obj-n obj-
这些变量代替了 $(O_OBJS)、$(OX_OBJS)、$(M_OBJS) 和 $(MX_OBJS)。 
例如:
# drivers/block/Makefile obj-$(CONFIG_MAC_FLOPPY) += swim3.o obj-$(CONFIG_BLK_DEV_FD) += floppy.o obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o
请注意这里用 $(CONFIG_...) 在赋值操作符左侧进行替换。这使 GNU Make 具有组合索引的能力!每个这样的赋值替换了旧风格 Makefile 中的八行代码。 
在执行所有的赋值之后,子目录 Makefile 就创建了四个列表:$(obj-y)、$(obj-m)、 $(obj-n) 和 $(obj-)。 
$(obj-y) 是包含在 vmlinux 之中的文件的列表。 
$(obj-m) 是作为单独模块创建的文件列表。 
忽略 $(obj-n) 和 $(obj-)。 
每个列表都可能含有重复项目;此后重复的项目将被自动删除。同时出现在 $(obj-y) 和 $(obj-m) 中的文件将自动从 $(obj-m) 列表中删除。 
Example:
# drivers/net/Makefile ... obj-$(CONFIG_OAKNET) += oaknet.o 8390.o ... obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o ... obj-$(CONFIG_STNIC) += stnic.o 8390.o ... obj-$(CONFIG_MAC8390) += daynaport.o 8390.o ...
在这个例子中,四个不同的驱动程序都需要 8390.o 中的代码。如果这四个驱动程序中的一个或多个要创建到 vmlinux 之中,即使其它驱动程序需要 8390.o 作为模块进行创建,8390.o 就会被创建到 vmlinux 之中而不是成为一个模块。(模块化的驱动程序能够使用常驻于 vmlinux 映像中的 8390.o 代码提供的服务)。
export-objs
$(export-objs) 是子目录中所有可能导出符号的文件的列表。构造该列表的规范方法是: grep -l EXPORT_SYMBOL *.c
(但看看那些在引入头文件中偷偷调用 EXPORT_SYMBOL 的文件吧!) 
这是个可能的列表,与内核配置无关。$(export-objs) 列举出所有导出符号的文件。而后样板代码就用 $(export-objs) 来将实际的文件列表划分为 $(*_OBJS) 和 $(*X_OBJS)。 
经验表明在旧风格 Makefile 中维护一个正确的 X 变量有困难并容易出错。在新风格 Makefile 中维护 $(export-objs) 比较简单并便于核对。
$(foo)-objs
某些内核模块由多个目标文件连接组成。 
对于每个多部分内核模块,都有一个列表列出所有组成该模块的目标文件。对于名为 foo.o 的内核模块,它的目标文件列表就是 foo-objs。 
例如:
# drivers/scsi/Makefile list-multi := scsi_mod.o sr_mod.o initio.o a100u2w.o ... scsi_mod-objs := hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o scsi_error.o scsi_obsolete.o scsi_queue.o scsi_lib.o scsi_merge.o scsi_dma.o scsi_scan.o scsi_syms.o sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o initio-objs := ini9100u.o i91uscsi.o a100u2w-objs := inia100.o i60uscsi.o
子目录 Makefile 将以常见的配置相关方式将模块加入 obj-* 列表: obj-$(CONFIG_SCSI) += scsi_mod.o obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o obj-$(CONFIG_SCSI_INITIO) += initio.o obj-$(CONFIG_SCSI_INIA100) += a100u2w.o
假定 CONFIG_SCSI=y。那么 vmlinux 就需要把它的 14 组件连接为 scsi_mod.o。 
假定 CONFIG_BLK_DEV_SR=m。那么 sr_mod.o 的三个组件就要以 “$(LD) -r” 连接到一起以组成内核模块 sr_mod.o。 
再假定 CONFIG_SCSI_INITIO=n。那么 initio.o 就进入 $(obj-n) 列表而且这就是它的终点了。不会编译它的组件文件,也不会创建组合文件。
subdir-y subdir-m subdir-n subdir-
这些变量替代了 $(ALL_SUB_DIRS)、$(SUB_DIRS) 和 $(MOD_SUB_DIRS)。 
例如:
# drivers/Makefile subdir-$(CONFIG_PCI) += pci subdir-$(CONFIG_PCMCIA) += pcmcia subdir-$(CONFIG_MTD) += mtd subdir-$(CONFIG_SBUS) += sbus
这些变量按类似于 obj-* 的方式工作,但用于子目录而不是目标文件。 
在所有赋值完成后,子目录 Makefile 就组成了四个列表:$(subdir-y)、$(subdir-m)、 $(subdir-n) 和 $(subdir-)。 
$(subdir-y) 是创建 vmlinux 时应该进入的子目录列表。 
$(subdir-m) 是创建模块时应该进入的子目录列表。 
$(subdir-n) 和 $(subdir-) 只用于收集本目录的所有子目录的列表。 
除 subdir-y 以外每个列表都可能含有重复项目;此后将自动删除重复项目。
mod-subdirs
$(mod-subdirs) 是一个所有即使出现在 $(subdir-y) 中也应该加入 $(subdir-m) 的子目录的列表。 
例如:
# fs/Makefile mod-subdirs := nls
这意味着如果 CONFIG_NLS=y,nls 应该加入 $(subdir-y) 和 $(subdir-m)。
9、感谢 linux-kbuild 邮件列表的成员审阅本文档,并对 Peter Samuelson 和 Thomas Molina 致以特别的感谢。

阅读(1139) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~