make的工作过程主要包括读取Makefile文件、确定依赖关系、执行生成命令等步骤。具体过程如下:
1、读取Makefile文件:make会在当前目录下寻找名为“Makefile”或“makefile”的文件,并读取该文件中的内容。
2、确定依赖关系:make会分析Makefile中的规则,为所有目标文件创建依赖关系链。这些规则说明了如何生成目标文件,以及目标文件依赖于哪些源文件或其他文件。
3、执行生成命令:根据依赖关系和时间数据,make会确定哪些文件需要重新生成,并执行相应的命令来更新这些文件。如果目标文件不存在,或者其依赖的文件比它新,make就会执行定义的命令来生成目标文件。
VERSION = 2
PATCHLEVEL = 4
SUBLEVEL = 20
EXTRAVERSION =
这一串代码定义了Linux内核的版本号。
VERSION表示主板本号,即主要版本号。
PATCHLEVEL表示次版本号,即次要版本号。
SUBLEVEL表示再次版本号,即更次要的版本号。
EXTRAVERSION表示额外的版本信息,通常用于标识特定的构建或修复。
这些版本信息共同构成了软件的完整版本标识,帮助用户和开发者了解软件的详细版本信息。例如,如果VERSION=2、PATCHLEVEL=4、SUBLEVEL=20、EXTRAVERSION=-rc1,那么这个版本的标识就是2.4.20-rc1
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
这行代码是Linux内核编译系统中的一个常见定义,它用于确定当前内核的版本号。KERNELRELEASE是一个环境变量,它在内核构建脚本中被定义,并且在构建内核时由Makefile传递。VERSION, PATCHLEVEL, SUBLEVEL, 和 EXTRAVERSION 是内核版本号的组成部分。VERSION 是主版本号,PATCHLEVEL 是次版本号,SUBLEVEL 是补丁级别,EXTRAVERSION 通常用于为内核添加特定的标识信息,比如测试版本或者是私人定制的版本。这个定义的含义是将内核版本号组合在一起,形成一个字符串,然后赋值给KERNELRELEASE环境变量。这个变量之后会在内核的Makefile中使用,以确定如何构建内核。通常,这个定义会在内核的顶层Makefile中出现,这样的定义让Makefile可以根据KERNELRELEASE变量的存在与否来决定是以内核构建的方式来处理Makefile,还是以普通的目标处理方式。如果KERNELRELEASE不存在,那么Makefile会处理其通常的目标,如果存在,则会按照构建内核的特定规则来处理。
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
Shell是用户与Linux系统交互的界面程序,它接收用户输入的命令并执行相应的操作。Shell通过解析用户输入的命令来执行对应的程序或脚本。在$(shell uname -m)这个表达式中,Shell首先执行uname -m命令。ARCH变量的定义是通过执行uname -m命令获取主机架构,并通过sed命令替换特定的字符串来实现的。具体来说,uname -m命令用于获取当前系统的硬件名称,例如x86_64。然后,通过管道传递给sed命令,sed命令的-e选项后面跟的是一串命令脚本,s/i.86/i386/表示将输入流中的i.86替换为i386。其中,.是一个通配符,表示任意字符。例如,将i.86替换为i386,其他类似的替换规则还包括将sun4u替换为sparc64,将arm.*替换为arm等。这样做的目的是为了确保ARCH变量能够准确地反映不同的处理器架构。
既然shell只是一个解释器,那么shell就有很多种,查看当前的shell类型:
在Linux终端中,输入一行“字符串”shell会检查这个字符串是否是内部命令,如果是内部命令,直接执行,如果不是内部命令,就去外部命令的存放路径找一下,查看是否有对应的可执行文件。shell内置的命令叫做内部命令,在用户登录的时候进行初始化到内存中的命令,在硬盘的指定路径存放的二进制可执行文件叫做外部命令。那么如何判断一个命令是内部命令还是外部命令呢?
shell中的外部命令是存放在固定的路径,$PATH这个变量就是存放着的外部命令二进制文件的存放路径,在寻找外部命令的时候,会一个路径一个路径的找,找到以后就结束查找。因此在终端输入一个字符串的时候,只要在$PATH路径项下有指定的二进制文件名称即可,并不一定非要是一个常规的命令。
KERNELPATH=kernel-$(shell echo $(KERNELRELEASE) | sed -e "s/-//g")
KERNELRELEASE是一个在内核Makefile中定义的变量,它代表了当前内核版本号。例如,对于Linux 4.15.0内核,KERNELRELEASE可能的值是4.15.0。shell是Makefile脚本中的一个函数,用于执行shell命令,并将命令的输出返回。sed -e "s/-//g"是一个sed命令,用于将字符串中的所有破折号(-)替换为空。所以,KERNELPATH=kernel-$(shell echo $(KERNELRELEASE) | sed -e "s/-//g")这行代码的含义是,如果KERNELRELEASE变量已经定义了(通常在内核的顶层Makefile中定义),那么就将其值(内核版本号)回显出来,并将其中的所有破折号替换为空。然后在前面加上kernel-,最终这个值就是内核模块的目标文件名。这种写法通常用于编译内核模块,因为它可以自动适应不同的内核版本。
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi ; fi)
这段代码是一个Makefile中的赋值语句,用于设置CONFIG_SHELL变量的值。这个变量的目的是确定使用哪个shell来执行配置脚本。下面是这段代码的逐步解释:
$(shell ...): 这是Makefile中的一个函数调用,shell函数用于执行其中的shell命令,并将命令的输出作为函数的返回值。
if [ -x "$$BASH" ]; then echo $$BASH;: 这部分检查环境变量BASH是否存在且可执行(-x是检查文件是否存在且有执行权限)。如果BASH满足条件,就输出BASH的值。
else if [ -x /bin/bash ]; then echo /bin/bash;: 如果环境变量BASH不满足条件,这段代码检查/bin/bash是否可执行。如果可执行,就输出/bin/bash。
else echo sh;: 如果上述两个条件都不满足,就输出sh,意味着使用标准的shell。
fi; fi: 这是if语句的结束标记。
总的来说,这段代码的目的是确定一个可用的shell来执行配置脚本,优先使用环境变量BASH指定的shell,如果不存在或不可用,则尝试使用/bin/bash,如果仍然不可用,最后使用sh。
TOPDIR := $(shell /bin/pwd)
TOPDIR:是变量名,用于存储当前目录的绝对路径。
:=:是 Makefile 中的一种赋值操作符,用于给变量赋值。:= 表示变量被赋予的值是在解析 Makefile 时就确定的,即该变量的值在 Makefile 被解析时就已经确定,不会随着 Makefile 的执行而改变。
$(shell /bin/pwd):是 Makefile 中的命令替换语法。shell 是 Makefile 的一个函数,用于执行 shell 命。/bin/pwd 是执行的 shell 命令,用于打印当前目录的绝对路径。$(...) 用于执行括号内的命令,并将命令的输出结果替换到当前位置。
综上所述,TOPDIR := $(shell /bin/pwd) 这行代码的作用是定义一个变量 TOPDIR,并将其值设置为当前目录的绝对路径。这在编写 Makefile 时非常有用,特别是当你需要在 Makefile 中引用当前目录的路径时。
HPATH = $(TOPDIR)/include
FINDHPATH = $(HPATH)/asm $(HPATH)/linux $(HPATH)/scsi $(HPATH)/net $(HPATH)/math-emu
HOSTCC = gcc
HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
CROSS_COMPILE =
HPATH:定义了一个路径变量,其值为$(TOPDIR)/include。这里$(TOPDIR)是一个变量,代表顶级目录的路径,include是顶级目录下的一个子目录,用于存放头文件。
FINDHPATH:定义了另一个路径变量,其值为多个路径的组合,这些路径都是基于HPATH变量的值。这些路径包括了asm、linux、scsi、net、math-emu等子目录,这些子目录用于存放特定类型的头文件。
HOSTCC:定义了用于编译主机程序的编译器,这里使用的是gcc。
HOSTCFLAGS:定义了编译主机程序时使用的编译器选项。这里的选项包括-Wall(显示所有警告)、-Wstrict-prototypes(检查函数声明与定义是否一致)、-O2(优化编译,使用一定的优化技术,但不保证最快的执行速度)、-fomit-frame-pointer(在可能的情况下省略帧指针,以减少内存占用)。
CROSS_COMPILE:这个变量没有给出具体的值,但它通常用于定义交叉编译时的编译器前缀。交叉编译是指在一个平台上编译代码,但目标运行平台不同。例如,在Linux平台上编译代码,但目标运行平台是ARM架构的设备。
总的来说,这段代码是用于定义编译过程中需要的路径和编译器选项。
#
# Include the make variables (CC, etc...)
#
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
MAKEFILES = $(TOPDIR)/.config
GENKSYMS = /sbin/genksyms
DEPMOD = /sbin/depmod
MODFLAGS = -DMODULE
CFLAGS_KERNEL =
PERL = perl
这段文本是Makefile或配置脚本的一部分,用于定义一些编译工具和它们的命令行前缀。这里解释一下每一行的含义:
AS = $(CROSS_COMPILE)as:定义汇编器(Assembler)的命令。$(CROSS_COMPILE)是一个变量,用于指定交叉编译工具链的前缀。如果编译目标是与编译主机不同的架构,这个前缀就会指向对应的交叉编译工具。
LD = $(CROSS_COMPILE)ld:定义链接器(Linker)的命令。
CC = $(CROSS_COMPILE)gcc:定义C编译器(C Compiler)的命令。
CPP = $(CC) -E:定义C预处理器(C Preprocessor)的命令,-E选项让gcc只运行预处理器。
AR = $(CROSS_COMPILE)ar:定义归档工具(Archiver)的命令,用于创建、修改以及提取静态库文件。
NM = $(CROSS_COMPILE)nm:定义nm工具的命令,用于列出对象文件或可执行文件的符号表。
STRIP = $(CROSS_COMPILE)strip:定义strip工具的命令,用于去除程序中的符号信息,减小文件大小。
OBJCOPY = $(CROSS_COMPILE)objcopy:定义objcopy工具的命令,用于复制和转换对象文件。
OBJDUMP = $(CROSS_COMPILE)objdump:定义objdump工具的命令,用于显示二进制文件的信息。
MAKEFILES = $(TOPDIR)/.config:定义Makefile文件的路径。
GENKSYMS = /sbin/genksyms:定义genksyms工具的路径,用于生成内核符号表。
DEPMOD = /sbin/depmod:定义depmod工具的路径,用于处理模块依赖。
MODFLAGS = -DMODULE:定义编译模块时使用的标志。
CFLAGS_KERNEL =:定义编译内核时使用的C语言标志,但这里没有给出具体的值。
PERL = perl:定义Perl解释器的命令。
这些定义通常用于Makefile中,以便在编译过程中引用这些工具。通过这种方式,可以很容易地修改工具链或添加交叉编译支持。
export VERSION PATCHLEVEL SUBLEVEL EXTRAVERSION KERNELRELEASE ARCH \
CONFIG_SHELL TOPDIR HPATH HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \
CPP AR NM STRIP OBJCOPY OBJDUMP MAKE MAKEFILES GENKSYMS MODFLAGS PERL
all: do-it-all
Makefile中的export用于导出变量。以下是关于Makefile中export作用的详细解释:
导出变量到子进程:export命令用于将变量从Makefile导出到由Makefile启动的子进程的环境中。这确保了在编译过程中启动的任何子shell或程序都能访问这些变量。
跨Makefile层级传递:在父Makefile中export出来的变量,可以在子Makefile(sub make)中访问。但同一级别的Makefile之间无法通过export来传递变量。
与Shell中的export区别:Makefile中的export是导出变量到子Makefile,而目标对应执行的动作中的export是属于Shell中的export,其作用是导出变量到当前Shell。这两个export的作用是不同的。
最后的all: do-it-all是一个目标规则,表示当执行make all命令时,会执行do-it-all这个目标,这通常意味着执行内核的完整构建过程。
#
# Make "config" the default target if there is no configuration file or
# "depend" the target if there is no top-level dependency information.
#
ifeq (.config,$(wildcard .config))
include .config
ifeq (.depend,$(wildcard .depend))
include .depend
do-it-all: Version vmlinux
else
CONFIGURATION = depend
do-it-all: depend
endif
else
CONFIGURATION = config
do-it-all: config
endif
在Makefile中,$(wildcard pattern) 是一个函数,用于列出当前目录下所有匹配指定模式(pattern)的文件名。这个函数在进行文件存在性检查或者构建文件列表时非常有用。当你看到 $(wildcard .config),这意味着Makefile正在查找当前目录下名为 .config 的文件。如果该文件存在,$(wildcard .config) 就会返回 .config;如果不存在,就会返回一个空字符串。这段代码是一个Makefile脚本的一部分,用于根据当前目录下是否存在.config或.depend文件来决定执行的操作。下面是对这段代码的详细解释:
ifeq (.config,$(wildcard .config)):这行检查当前目录下是否存在.config文件。$(wildcard .config)是一个Makefile的函数,用于列出所有匹配.config模式的文件名。如果.config文件存在,条件为真。
include .config:如果.config文件存在,这行命令会包含(即执行).config文件中的内容。
ifeq (.depend,$(wildcard .depend)):这行检查当前目录下是否存在.depend文件。同样地,$(wildcard .depend)用于列出所有匹配.depend模式的文件名。如果.depend文件存在,条件为真。
include .depend:如果.depend文件存在,这行命令会包含(即执行).depend文件中的内容。
do-it-all: Version vmlinux:这是一个目标定义,表示如果前面的条件都不满足,do-it-all目标会执行Version vmlinux命令。不过,这里的Version vmlinux看起来可能是一个占位符或错误,通常应该是具体的命令或脚本。
else CONFIGURATION = depend do-it-all: depend endif:如果.config文件不存在但.depend文件存在,设置CONFIGURATION变量为depend,并将do-it-all目标定义为执行depend命令。
else CONFIGURATION = config do-it-all: config endif:如果.config和.depend文件都不存在,设置CONFIGURATION变量为config,并将do-it-all目标定义为执行config命令。
总之,这段Makefile脚本的目的是根据当前目录下.config和.depend文件的存在情况来决定执行的操作。如果.config文件存在,就执行.config文件中的内容;如果.depend文件存在,就执行.depend文件中的内容;如果两个文件都不存在,就执行config命令。
#
# INSTALL_PATH specifies where to place the updated kernel and system map
# images. Uncomment if you want to place them anywhere other than root.
#
#export INSTALL_PATH=/boot
在 Linux 系统中,以 # 开头的行通常被视为注释,不会被执行。export INSTALL_PATH=/boot 是设置环境变量的命令。具体来说,这条命令的作用是将环境变量INSTALL_PATH的值设置为/boot。在Linux系统中,环境变量是操作系统中用来指定操作系统运行环境的一些参数,它们影响着程序的运行行为。通过设置环境变量,可以控制程序运行时的各种参数和配置。使用export命令设置的环境变量只在当前shell会话中有效,如果打开一个新的shell会话,之前设置的环境变量将不再有效,除非在用户的配置文件中(如.bashrc、.bash_profile等)进行永久设置。要查看当前设置的环境变量,可以使用export命令不带任何参数,或者使用echo $变量名的方式查看特定环境变量的值。
#
# INSTALL_MOD_PATH specifies a prefix to MODLIB for module directory
# relocations required by build roots. This is not defined in the
# makefile but the arguement can be passed to make if needed.
#
MODLIB := $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)
export MODLIB
这段代码是一个Makefile脚本的一部分,用于定义和导出MODLIB变量。MODLIB变量用于指定模块目录的路径,这个路径是基于INSTALL_MOD_PATH和内核版本(KERNELRELEASE)构建的。下面是对这段代码的详细解释:
注释:首先,这段代码开头的注释解释了INSTALL_MOD_PATH的作用。它指定了一个前缀,用于模块目录的重定位,这在构建根目录时可能是必需的。
INSTALL_MOD_PATH:这是一个变量,它没有被在这段代码中定义,但可以通过make命令的参数传递给它一个值。这个变量用于指定一个路径前缀。
MODLIB变量的定义:MODLIB是通过连接INSTALL_MOD_PATH、/lib/modules/和KERNELRELEASE这三个部分来定义的。KERNELRELEASE是另一个变量,代表当前内核的版本。这样,MODLIB变量就代表了模块目录的完整路径。
export MODLIB:这行命令将MODLIB变量导出,使其在当前Makefile的后续部分以及任何子Makefile中都是可见的。这是必要的,因为MODLIB变量可能会在多个Makefile中被引用。
总的来说,这段代码的目的是为了灵活地定义和导出模块目录的路径,使得在构建内核模块时可以根据需要轻松地改变模块的安装位置。
#
# standard CFLAGS
#
CPPFLAGS := -D__KERNEL__ -I$(HPATH)
CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \
-fno-strict-aliasing -fno-common
ifndef CONFIG_FRAME_POINTER
CFLAGS += -fomit-frame-pointer
endif
AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS)
这段代码是在设置编译时的标志(flags),用于指导编译器如何编译代码。下面是对这段代码的逐行解释:
CPPFLAGS := -D__KERNEL__ -I$(HPATH):
CPPFLAGS 是预处理器标志的变量,用于C和C++的预处理阶段。
-D__KERNEL__ 是定义一个宏__KERNEL__,告诉代码这是在内核模式下编译。
-I$(HPATH) 是添加一个包含(include)目录,$(HPATH)是一个变量,代表具体的路径。
CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fno-strict-aliasing -fno-common:
CFLAGS 是C编译器的标志变量。
-Wall 打开警告信息。
-Wstrict-prototypes 警告关于函数原型的问题。
-Wno-trigraphs 关闭三字符序列的警告。
-O2 设置优化级别为2。
-fno-strict-aliasing 关闭严格别名规则,允许不同类型的指针指向相同内存地址。
-fno-common 禁止对未初始化的全局变量使用common存储类型。
ifndef CONFIG_FRAME_POINTER:这行检查是否没有定义CONFIG_FRAME_POINTER宏。
CFLAGS += -fomit-frame-pointer:
如果没有定义CONFIG_FRAME_POINTER,则在CFLAGS中添加-fomit-frame-pointer标志,这个标志用于省略帧指针,以节省一些内存和寄存器。
endif:结束ifndef条件。
AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS):
AFLAGS 是汇编器标志的变量。
-D__ASSEMBLY__ 定义一个宏__ASSEMBLY__,用于汇编代码。
$(CPPFLAGS) 包含前面定义的预处理器标志。
这段代码主要用于设置内核编译时的标志,包括预处理器标志、C编译器标志和汇编器标志。
#
# ROOT_DEV specifies the default root-device when making the image.
# This can be either FLOPPY, CURRENT, /dev/xxxx or empty, in which case
# the default of FLOPPY is used by 'build'.
# This is i386 specific.
#
export ROOT_DEV = CURRENT
ROOT_DEV 被描述为一个环境变量,它指定了在制作镜像时的默认根设备。这个变量可以有几种不同的值:
FLOPPY:一个特定的值,可能代表某种类型的软盘设备。
CURRENT:表示使用当前的根设备。
/dev/xxxx:一个具体的设备文件路径,xxxx 应该被替换为实际的设备名。
空值:如果不设置这个变量,build 命令可能会使用 FLOPPY 作为默认值。
注释还指出,这是特定于 i386 架构的。
然而,注释中的 export ROOT_DEV = CURRENT 并不是有效的 shell 语法。正确的设置环境变量的方式应该是没有空格的,像这样:export ROOT_DEV=CURRENT,在 shell 脚本或命令行中,您应该使用这种没有空格的语法来设置环境变量。如果您在脚本或配置文件中看到 export ROOT_DEV = CURRENT,那么这是一个错误,应该将其更正为 export ROOT_DEV=CURRENT。
#
# If you want to preset the SVGA mode, uncomment the next line and
# set SVGA_MODE to whatever number you want.
# Set it to -DSVGA_MODE=NORMAL_VGA if you just want the EGA/VGA mode.
# The number is the same as you would ordinarily press at bootup.
# This is i386 specific.
#
export SVGA_MODE = -DSVGA_MODE=NORMAL_VGA
这段代码是一个注释,用于解释如何在启动时预设SVGA模式。如果你想要预设SVGA模式,可以取消下一行的注释,并将SVGA_MODE设置为你想要的任何数字。如果你只是想要EGA/VGA模式,可以将其设置为-DSVGA_MODE=NORMAL_VGA。这个数字与你通常在启动时按的数字相同。这是特定于i386架构的。export SVGA_MODE = -DSVGA_MODE=NORMAL_VGA这行是一个示例,注意在实际使用中,等号两边不应有空格。
#
# If you want the RAM disk device, define this to be the size in blocks.
# This is i386 specific.
#
#export RAMDISK = -DRAMDISK=512
这段代码同样是一个注释,用于解释如何定义RAM磁盘设备的大小。如果你想要使用RAM磁盘设备,可以将RAMDISK定义为你想要的大小,单位是块(blocks)。这是特定于i386架构的。示例中的#export RAMDISK = -DRAMDISK=512表示如何设置RAM磁盘大小为512块,但注意在实际使用中,等号两边不应有空格,且应该去掉行首的#来取消注释。正确的设置方式应该是export RAMDISK=-DRAMDISK=512。
CORE_FILES =kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o
NETWORKS =net/network.o
LIBS =$(TOPDIR)/lib/lib.a
SUBDIRS =kernel drivers mm fs net ipc lib
DRIVERS-n :=
DRIVERS-y :=
DRIVERS-m :=
DRIVERS- :=
DRIVERS-$(CONFIG_ACPI) += drivers/acpi/acpi.o
DRIVERS-$(CONFIG_PARPORT) += drivers/parport/driver.o
DRIVERS-y += drivers/char/char.o \
drivers/block/block.o \
drivers/misc/misc.o \
drivers/net/net.o \
drivers/media/media.o
DRIVERS-$(CONFIG_AGP) += drivers/char/agp/agp.o
DRIVERS-$(CONFIG_DRM_NEW) += drivers/char/drm/drm.o
DRIVERS-$(CONFIG_DRM_OLD) += drivers/char/drm-4.0/drm.o
DRIVERS-$(CONFIG_NUBUS) += drivers/nubus/nubus.a
DRIVERS-$(CONFIG_NET_FC) += drivers/net/fc/fc.o
DRIVERS-$(CONFIG_DEV_APPLETALK) += drivers/net/appletalk/appletalk.o
DRIVERS-$(CONFIG_TR) += drivers/net/tokenring/tr.o
DRIVERS-$(CONFIG_WAN) += drivers/net/wan/wan.o
DRIVERS-$(CONFIG_ARCNET) += drivers/net/arcnet/arcnetdrv.o
DRIVERS-$(CONFIG_ATM) += drivers/atm/atm.o
DRIVERS-$(CONFIG_IDE) += drivers/ide/idedriver.o
DRIVERS-$(CONFIG_FC4) += drivers/fc4/fc4.a
DRIVERS-$(CONFIG_SCSI) += drivers/scsi/scsidrv.o
DRIVERS-$(CONFIG_FUSION_BOOT) += drivers/message/fusion/fusion.o
DRIVERS-$(CONFIG_IEEE1394) += drivers/ieee1394/ieee1394drv.o
ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR)$(CONFIG_PARIDE_PCD),)
DRIVERS-y += drivers/cdrom/driver.o
endif
DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o
DRIVERS-$(CONFIG_PCI) += drivers/pci/driver.o
DRIVERS-$(CONFIG_MTD) += drivers/mtd/mtdlink.o
DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o
DRIVERS-$(CONFIG_NET_PCMCIA) += drivers/net/pcmcia/pcmcia_net.o
DRIVERS-$(CONFIG_NET_WIRELESS) += drivers/net/wireless/wireless_net.o
DRIVERS-$(CONFIG_PCMCIA_CHRDEV) += drivers/char/pcmcia/pcmcia_char.o
DRIVERS-$(CONFIG_DIO) += drivers/dio/dio.a
DRIVERS-$(CONFIG_SBUS) += drivers/sbus/sbus_all.o
DRIVERS-$(CONFIG_ZORRO) += drivers/zorro/driver.o
DRIVERS-$(CONFIG_FC4) += drivers/fc4/fc4.a
DRIVERS-$(CONFIG_PPC32) += drivers/macintosh/macintosh.o
DRIVERS-$(CONFIG_MAC) += drivers/macintosh/macintosh.o
DRIVERS-$(CONFIG_ISAPNP) += drivers/pnp/pnp.o
DRIVERS-$(CONFIG_SGI_IP22) += drivers/sgi/sgi.a
DRIVERS-$(CONFIG_VT) += drivers/video/video.o
DRIVERS-$(CONFIG_PARIDE) += drivers/block/paride/paride.a
DRIVERS-$(CONFIG_HAMRADIO) += drivers/net/hamradio/hamradio.o
DRIVERS-$(CONFIG_TC) += drivers/tc/tc.a
DRIVERS-$(CONFIG_USB) += drivers/usb/usbdrv.o
DRIVERS-$(CONFIG_INPUT) += drivers/input/inputdrv.o
DRIVERS-$(CONFIG_HIL) += drivers/hil/hil.o
DRIVERS-$(CONFIG_I2O) += drivers/message/i2o/i2o.o
DRIVERS-$(CONFIG_IRDA) += drivers/net/irda/irda.o
DRIVERS-$(CONFIG_I2C) += drivers/i2c/i2c.o
DRIVERS-$(CONFIG_PHONE) += drivers/telephony/telephony.o
DRIVERS-$(CONFIG_MD) += drivers/md/mddev.o
DRIVERS-$(CONFIG_GSC) += drivers/gsc/gscbus.o
DRIVERS-$(CONFIG_BLUEZ) += drivers/bluetooth/bluetooth.o
DRIVERS-$(CONFIG_HOTPLUG_PCI) += drivers/hotplug/vmlinux-obj.o
DRIVERS-$(CONFIG_ISDN_BOOL) += drivers/isdn/vmlinux-obj.o
DRIVERS := $(DRIVERS-y)
这段代码是一个Makefile片段,用于定义Linux内核或类似系统的构建过程。它主要涉及到驱动程序(DRIVERS)的编译配置。下面是对这段代码的解析:
变量定义:
CORE_FILES:定义了核心文件列表。
NETWORKS:定义了网络相关的文件列表。
LIBS:定义了库文件的位置。
SUBDIRS:定义了子目录列表,这些子目录包含了要编译的源代码。
驱动程序配置:
DRIVERS-n、DRIVERS-y、DRIVERS-m:这些变量通常用于控制哪些驱动程序被编译进内核(y表示是,n表示否,m表示模块)。但在这段代码中,它们被初始化为空。
DRIVERS-:这是一个中间变量,用于根据配置选项动态构建驱动程序列表。
DRIVERS-$(CONFIG_XXX):这些行根据特定的配置选项(如CONFIG_ACPI、CONFIG_PARPORT等)向DRIVERS-变量添加相应的驱动程序文件。
条件编译:
ifneq:这是一个条件语句,用于检查四个配置选项(CONFIG_CD_NO_IDESCSI、CONFIG_BLK_DEV_IDECD、CONFIG_BLK_DEV_SR、CONFIG_PARIDE_PCD)中是否有任何一个被定义。如果这些配置选项中的任何一个被定义(即其值为非空),则条件为真,就向DRIVERS-y添加相应的驱动程序文件。
其他驱动程序配置:
类似于上面的DRIVERS-$(CONFIG_XXX)行,这部分代码根据其他配置选项添加更多的驱动程序文件到DRIVERS-y或其他相关的变量中。
重复定义:
注意,DRIVERS-$(CONFIG_FC4)被重复定义了两次,这可能是一个错误或冗余。在实际使用中,应该避免这种情况。
最终驱动程序列表:
DRIVERS := $(DRIVERS-y):这行代码将DRIVERS-y变量的值赋给DRIVERS变量。DRIVERS-y通常包含了所有要编译进内核的驱动程序文件列表。
语法和格式:
Makefile的语法非常严格,每一行都必须正确格式化。例如,变量赋值时等号两边不能有空格,条件语句和循环语句也必须按照特定的语法编写。
这段代码的主要目的是根据内核配置选项动态构建要编译的驱动程序列表。这是Linux内核构建系统的一个典型例子,展示了如何根据不同的配置选项来包含或排除特定的代码部分。
# files removed with 'make clean'
CLEAN_FILES = \
kernel/ksyms.lst include/linux/compile.h \
vmlinux System.map \
.tmp* \
drivers/char/consolemap_deftbl.c drivers/video/promcon_tbl.c \
drivers/char/conmakehash \
drivers/char/drm/*-mod.c \
drivers/pci/devlist.h drivers/pci/classlist.h drivers/pci/gen-devlist \
drivers/zorro/devlist.h drivers/zorro/gen-devlist \
drivers/sound/bin2hex drivers/sound/hex2hex \
drivers/atm/fore200e_mkfirm drivers/atm/{pca,sba}*{.bin,.bin1,.bin2} \
drivers/scsi/aic7xxx/aicasm/aicasm \
drivers/scsi/aic7xxx/aicasm/aicasm_gram.c \
drivers/scsi/aic7xxx/aicasm/aicasm_gram.h \
drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.c \
drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.h \
drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.c \
drivers/scsi/aic7xxx/aicasm/aicasm_scan.c \
drivers/scsi/aic7xxx/aicasm/aicdb.h \
drivers/scsi/aic7xxx/aicasm/y.tab.h \
drivers/scsi/53c700_d.h \
net/khttpd/make_times_h \
net/khttpd/times.h \
submenu*
这段内容是一个makefile文件中的一部分,它定义了在执行make clean命令时应该删除的文件和目录。make clean命令通常用于清理编译过程中产生的临时文件和编译产物,以确保下一次编译是从一个干净的状态开始的。
在这个CLEAN_FILES变量中,列出了多种类型的文件,包括:
编译生成的列表和头文件,如kernel/ksyms.lst和include/linux/compile.h。
内核映像文件,如vmlinux和System.map。
临时文件,如.tmp*。
特定于驱动的源代码和编译产物,如drivers/char/consolemap_deftbl.c和drivers/pci/gen-devlist。
特定于硬件或子系统的工具和编译产物,如drivers/sound/bin2hex和drivers/atm/fore200e_mkfirm。
网络相关的编译产物,如net/khttpd/make_times_h。
自动生成的菜单文件,如submenu*。
make clean命令执行时,会删除这些文件和目录,从而确保下一次编译是从一个清晰、无旧编译产物的环境中开始的。这是软件开发中常见的做法,用于避免潜在的编译问题。
# directories removed with 'make clean'
CLEAN_DIRS = \
modules
在makefile中,CLEAN_DIRS变量定义了执行make clean命令时应该删除的目录。在这个具体的例子中,CLEAN_DIRS被设置为只包含一个目录:modules。
modules目录通常包含内核模块的编译产物。内核模块是可以在运行时被加载和卸载的内核代码段,它们提供了额外的功能,而不需要重新编译整个内核。当执行make clean命令时,makefile中的规则会删除CLEAN_DIRS变量指定的所有目录及其内容。在这个例子中,它会删除modules目录及其下的所有文件,从而清理掉所有内核模块的编译产物。
这样做可以确保下一次编译内核模块时,是从一个干净的状态开始的,避免了潜在的编译问题或由于旧文件残留导致的不一致性问题。
# files removed with 'make mrproper'
MRPROPER_FILES = \
include/linux/autoconf.h include/linux/version.h \
drivers/net/hamradio/soundmodem/sm_tbl_{afsk1200,afsk2666,fsk9600}.h \
drivers/net/hamradio/soundmodem/sm_tbl_{hapn4800,psk4800}.h \
drivers/net/hamradio/soundmodem/sm_tbl_{afsk2400_7,afsk2400_8}.h \
drivers/net/hamradio/soundmodem/gentbl \
drivers/sound/*_boot.h drivers/sound/.*.boot \
drivers/sound/msndinit.c \
drivers/sound/msndperm.c \
drivers/sound/pndsperm.c \
drivers/sound/pndspini.c \
drivers/atm/fore200e_*_fw.c drivers/atm/.fore200e_*.fw \
.version .config* config.in config.old \
scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp \
scripts/lxdialog/*.o scripts/lxdialog/lxdialog \
.menuconfig.log \
#include/asm \
.hdepend scripts/mkdep scripts/split-include scripts/docproc \
$(TOPDIR)/include/linux/modversions.h \
kernel.spec
make mrproper 命令用于彻底清理 Linux 内核源代码树,确保所有的配置文件和自动生成的文件都被删除,从而恢复到一个干净的状态。这个命令通常在准备重新配置内核之前使用。在您提供的列表中,MRPROPER_FILES 变量列出了 make mrproper 命令会删除的文件和目录。这些文件和目录包括:
include/linux/autoconf.h 和 include/linux/version.h:这两个文件是内核配置过程中自动生成的,包含了内核的配置信息和版本号。
drivers/net/hamradio/soundmodem/ 目录下的多个头文件:这些文件与业余无线电声音调制解调器相关,也是自动生成的。
drivers/sound/ 目录下的多个文件和目录:这些与声音驱动相关,包括启动文件和一些特定驱动的源代码文件。
.version、.config*、config.in、config.old:这些是与内核配置相关的文件,记录了内核的配置状态和历史。
scripts/ 目录下的多个脚本和临时文件:这些脚本用于内核构建过程中的各种任务,如解析配置文件、生成文档等。
.menuconfig.log:这个文件记录了使用菜单配置界面时的日志。
include/asm:实际上 make mrproper 不会删除 include/asm 目录,这里可能是个误写或格式错误。
.hdepend、scripts/mkdep、scripts/split-include、scripts/docproc:这些是与依赖管理、文档处理相关的脚本和文件。
$(TOPDIR)/include/linux/modversions.h:这个文件包含了模块版本信息,用于模块之间的兼容性检查。
kernel.spec:这个文件可能包含了内核构建的规范或说明。
执行 make mrproper 后,上述文件和目录都将被删除,确保内核源代码树处于一个干净的状态,为重新配置和构建内核做准备。
# directories removed with 'make mrproper'
MRPROPER_DIRS = \
include/config \
$(TOPDIR)/include/linux/modules
make mrproper 命令在 Linux 内核源码编译过程中用于彻底清理所有生成的文件,包括配置文件和编译生成的文件,确保源码目录回到初始状态。这个命令比 make clean 更彻底,make clean 只是删除大部分编译生成的文件,但保留配置文件。
MRPROPER_DIRS 变量定义了 make mrproper 命令需要清理的目录列表。这里列出了两个目录:
include/config:这个目录通常包含内核配置信息,如 .config 文件和自动生成的配置文件。
$(TOPDIR)/include/linux/modules:这个目录可能包含与模块相关的自动生成的头文件或其他文件。$(TOPDIR) 是一个 Makefile 变量,指向内核源码的顶层目录。
执行 make mrproper 时,这些目录及其内容会被删除,以确保源码目录完全清理,没有留下任何编译或配置的痕迹。这对于确保从一个干净的状态开始新的编译过程非常有用。
include arch/$(ARCH)/Makefile
在 Linux 内核的 Makefile 系统中,include arch/$(ARCH)/Makefile 这一行代码的作用是将特定架构($(ARCH))的 Makefile 包含到当前的 Makefile 中。这里,$(ARCH) 是一个变量,它代表了目标架构,比如 x86、arm、arm64 等。每个架构的目录(如 arch/x86/、arch/arm/ 等)下通常都会有一个 Makefile,它定义了该架构特有的编译规则、源文件、链接选项等。通过包含这个 Makefile,主 Makefile 能够根据目标架构来定制编译过程。这种机制使得 Linux 内核能够支持多种不同的硬件架构,因为不同的架构可能需要不同的源文件、编译选项或链接脚本。通过简单地改变 $(ARCH) 变量的值,开发者可以为不同的架构编译内核,而无需修改主 Makefile。总的来说,include arch/$(ARCH)/Makefile 这一行代码是 Linux 内核 Makefile 系统中实现跨架构支持的关键部分之一。
# Extra cflags for kbuild 2.4. The default is to forbid includes by kernel code
# from user space headers. Some UML code requires user space headers, in the
# UML Makefiles add 'kbuild_2_4_nostdinc :=' before include Rules.make. No
# other kernel code should include user space headers, if you need
# 'kbuild_2_4_nostdinc :=' or -I/usr/include for kernel code and you are not UML
# then your code is broken! KAO.
kbuild_2_4_nostdinc := -nostdinc -iwithprefix include
export kbuild_2_4_nostdinc
export CPPFLAGS CFLAGS CFLAGS_KERNEL AFLAGS AFLAGS_KERNEL
export NETWORKS DRIVERS LIBS HEAD LDFLAGS LINKFLAGS MAKEBOOT ASFLAGS
这段代码是关于Linux内核构建系统(kbuild)的配置,特别是在版本2.4中的额外cflags(编译器标志)设置。下面是对这段代码的解释:
默认设置:默认情况下,内核代码不允许包含用户空间的头文件。这是为了确保内核代码的纯净性和安全性,防止由于用户空间头文件的问题导致内核崩溃或安全问题。
UML的特殊情况:UML(用户模式Linux)是一种特殊的内核配置,它允许内核代码包含用户空间的头文件。如果在UML的Makefile中需要包含用户空间的头文件,可以在包含Rules.make之前添加kbuild_2_4_nostdinc :=来覆盖默认设置。
禁止非UML内核代码包含用户空间头文件:如果非UML的内核代码需要kbuild_2_4_nostdinc :=或-I/usr/include来包含用户空间的头文件,这表示代码存在问题。内核代码不应该依赖用户空间的头文件,否则可能会导致不稳定或安全问题。
变量定义:
kbuild_2_4_nostdinc := -nostdinc -iwithprefix include:定义了用于编译内核的额外cflags,-nostdinc表示不允许包含标准库的头文件,-iwithprefix include表示只包含以include为前缀的头文件路径。
export语句:导出了一些变量,包括kbuild_2_4_nostdinc、CPPFLAGS、CFLAGS、CFLAGS_KERNEL、AFLAGS、AFLAGS_KERNEL等,这些变量在构建内核时会被用到。
总的来说,这段代码是关于Linux内核构建系统在版本2.4时的配置,特别是关于如何处理头文件包含的问题,以确保内核代码的安全和纯净。
.S.s:
$(CPP) $(AFLAGS) $(AFLAGS_KERNEL) -traditional -o $*.s $<
这行Makefile规则定义了如何处理汇编源文件(.S 文件)以生成汇编语言输出文件(.s 文件)。下面是这行规则的详细解释:
.S.s:这是一个模式规则,用于匹配所有以 .S 结尾的文件,并为它们生成以 .s 结尾的目标文件。
$(CPP):这是Makefile中的一个变量,代表C预处理器(Preprocessor)的命令。C预处理器用于处理源代码文件中的预处理指令,如宏定义、文件包含等。
$(AFLAGS) 和 $(AFLAGS_KERNEL):这两个变量包含了汇编器(Assembler)的标志(flags)和选项。$(AFLAGS) 可能是通用的汇编器标志,而 $(AFLAGS_KERNEL) 则是特定于内核构建的汇编器标志。
-traditional:这是一个汇编器的标志,指示汇编器使用传统的语法和行为。这个标志的具体含义可能因汇编器的不同而有所差异。
-o $*.s:-o 是输出文件的选项,$*.s 指定了输出文件的名称。在这里,$* 表示匹配到的源文件(.S 文件)的名称部分(不包含扩展名),然后加上 .s 后缀作为输出文件的名称。
$<:这是一个自动变量,代表规则中的第一个依赖文件。在这个规则中,它表示当前的 .S 源文件。
综上所述,这行Makefile规则的作用是:对于每一个 .S 源文件,使用C预处理器和汇编器的标志来处理它,并生成一个对应的 .s 汇编语言输出文件。
.S.o:
$(CC) $(AFLAGS) $(AFLAGS_KERNEL) -traditional -c -o $*.o $<
这行命令是在一个Makefile文件中使用的,用于编译C语言源代码文件。下面是对这行命令的逐部分解释:
.S.o::这是一个模式规则,用于匹配所有以.S结尾的文件,并将它们编译成以.o结尾的目标文件。
$(CC):这是一个Makefile变量,代表C编译器的名称,比如gcc或clang。
$(AFLAGS):这是另一个Makefile变量,包含一组传递给编译器的汇编器标志(Assembler Flags)。
$(AFLAGS_KERNEL):这是另一个Makefile变量,可能包含特定于内核编译的汇编器标志。
-traditional:这是一个编译器选项,用于告诉编译器使用传统的编译行为。具体效果取决于所使用的编译器。
-c:这个选项告诉编译器只编译和汇编,但不链接。
-o $*.o:这指定了输出文件的名称。$*代表匹配到的文件名部分(不包括扩展名),所以$*.o就是生成的.o文件的名称。
$<:这是一个自动变量,代表规则中的第一个依赖文件。
综上所述,这行命令的意思是:对于所有以.S结尾的文件,使用指定的C编译器和汇编器标志,以传统的编译行为编译它们,生成以.o结尾的目标文件。
Version: dummy
@rm -f include/linux/compile.h
这行命令看起来是一个Makefile中的规则,用于在编译过程中删除一个特定的头文件。下面是对这个命令的详细解释:
Version: dummy:这部分可能是一个注释或者是一个特定的标记,用于标识这个规则的目的或者版本。dummy通常意味着这是一个占位符或者是一个不执行实际操作的规则。
@rm -f include/linux/compile.h:这是实际执行的命令。
@:在Makefile中,命令前的@字符用来控制命令的回显。默认情况下,make会打印每条命令然后再执行它。如果命令前加了@,make只会执行命令而不打印它。
rm -f:这是一个Unix/Linux命令,用于删除文件。-f选项表示“force”,即强制删除文件,即使文件不存在也不会报错。
include/linux/compile.h:这是要删除的文件的路径和名称。这个文件可能是在之前的编译过程中生成的,现在需要被删除以确保编译环境的清洁。
综上所述,这行命令的目的是在编译过程的某个阶段删除include/linux/compile.h文件,而且不会在控制台打印出这条删除命令。这通常用于清理旧的或者不再需要的编译生成文件。
boot: vmlinux
@$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C arch/$(ARCH)/boot
这行命令是一个Makefile中的规则,用于编译Linux内核的启动文件。下面是对这个命令的详细解释:
boot: vmlinux:这部分定义了一个规则,目标(target)是boot,依赖(dependency)是vmlinux。这意味着在执行boot规则之前,必须先确保vmlinux是存在的或者已经被更新。
@$(MAKE):这里$(MAKE)是一个Makefile变量,通常被设置为make命令的路径。前面的@字符用于控制命令的回显,如之前所述,它表示在执行命令时不打印命令本身。
CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)":这部分设置了编译时的C语言标志。$(CFLAGS)是一个Makefile变量,包含了基本的C编译器标志。$(CFLAGS_KERNEL)是另一个变量,包含了特定于内核编译的C编译器标志。这两个变量被合并起来,作为编译时的标志。
-C arch/$(ARCH)/boot:这部分指定了编译时使用的Makefile的路径。-C选项告诉make命令在指定的目录下查找Makefile并执行。arch/$(ARCH)/boot是一个路径,其中$(ARCH)是一个Makefile变量,代表目标架构(如x86、arm等)。这意味着make将在arch/<架构>/boot目录下查找并执行Makefile。
综上所述,这行命令的目的是使用特定的C编译器标志,在指定架构的boot目录下编译Linux内核的启动文件。这通常是内核编译过程中的一个步骤,用于生成内核的启动映像或其他相关文件。
vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o init/do_mounts.o linuxsubdirs
$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o init/do_mounts.o \
--start-group \
$(CORE_FILES) \
$(DRIVERS) \
$(NETWORKS) \
$(LIBS) \
--end-group \
-o vmlinux
$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map
这行命令是Makefile中的一个规则,用于编译Linux内核并生成vmlinux文件,这是内核的未压缩可执行映像。下面是对这个命令的详细解释:
vmlinux::这是规则的目标(target),表示要生成的文件是vmlinux。
include/linux/version.h $(CONFIGURATION) ...:这些是规则的依赖(dependency)。在生成vmlinux之前,必须确保这些文件存在或已被更新。include/linux/version.h包含了内核的版本信息,$(CONFIGURATION)可能是一个变量,代表内核的配置。
init/main.o init/version.o init/do_mounts.o linuxsubdirs:这些是内核编译过程中生成的中间对象文件(.o文件),以及linuxsubdirs变量可能代表的其他子目录中的对象文件。
$(LD) $(LINKFLAGS) $(HEAD):这些是链接器(Linker)的调用和相关的链接标志。$(LD)是链接器的路径,$(LINKFLAGS)是链接时的标志,$(HEAD)可能是一个包含链接时所需头部信息的变量。
init/main.o init/version.o init/do_mounts.o \ --start-group \ $(CORE_FILES) \ $(DRIVERS) \ $(NETWORKS) \ $(LIBS) \ --end-group \ -o vmlinux:这部分是链接命令的实际参数,指定了要链接的对象文件和库,以及输出文件vmlinux。在Makefile规则中,--start-group 和 --end-group 是链接器(如 GNU ld)的选项,用于控制链接过程中库文件的搜索顺序。这两个选项通常与一组库文件一起使用,以优化链接过程并可能减少最终生成的可执行文件的大小。具体来说:
--start-group:告诉链接器开始一个库文件组。在这个组内的库文件将被视为一个整体,链接器会在这个组内搜索符号依赖,而不是在遇到一个未解析的符号时就立即停止并报错。
--end-group:标记库文件组的结束。链接器在处理完这个组内的所有库文件后,将继续处理后续的输入文件。在这两个选项之间的库文件(如 $(CORE_FILES)、$(DRIVERS)、$(NETWORKS)、$(LIBS))将被视为一个组,链接器会在这个组内尝试解析所有未解析的符号。这有助于处理库文件之间的循环依赖,或者当多个库文件都定义了相同的符号时,确保正确的符号被选中。
例如,如果 $(CORE_FILES) 中的一个文件需要 $(DRIVERS) 中的一个符号,而 $(DRIVERS) 又需要 $(CORE_FILES) 中的一个符号,那么使用 --start-group 和 --end-group 可以确保链接器能够正确地解析这些相互依赖的符号。
$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map:这行命令是在Linux内核编译过程中用于生成System.map文件的。System.map是一个符号表,它包含了内核中所有符号(如函数、变量等)的地址信息。这个文件对于内核的调试和性能分析非常重要。下面是这行命令的详细解释:
$(NM) vmlinux:$(NM)是一个Makefile变量,通常被设置为nm工具的路径。nm是一个列出二进制文件(在这里是vmlinux)中符号的工具。这个命令会输出vmlinux中所有符号的名称和地址。
| grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)':这个管道命令使用grep工具来过滤nm的输出。-v选项表示选取不匹配的行。正则表达式'\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)'用于匹配并排除掉不需要的符号。这些符号可能包括编译时生成的临时符号、与对象文件(.o文件)相关的符号、以及某些特定的内核内部符号。
| sort:这个管道命令使用sort工具对过滤后的符号进行排序。排序后的符号更容易在System.map文件中查找和阅读。
> System.map:这个重定向命令将排序后的符号输出到System.map文件中。如果文件已经存在,它会被覆盖。
这行命令的目的是使用nm工具列出vmlinux中的所有符号,过滤掉不需要的符号,对剩余的符号进行排序,并将结果保存到System.map文件中。这个文件在内核的调试和性能分析过程中非常有用,因为它提供了符号和地址之间的映射关系。
综上所述,这行命令的目的是编译和链接Linux内核的各个部分,生成vmlinux文件,并创建一个包含内核符号信息的System.map文件。这是内核编译过程中的一个关键步骤。
symlinks:
rm -f include/asm
( cd include ; ln -sf asm-$(ARCH) asm)
@if [ ! -d include/linux/modules ]; then \
mkdir include/linux/modules; \
fi
这段代码是用于在Linux系统中处理符号链接(symlinks)和目录创建的shell脚本片段。让我们逐行解析这段代码:
symlinks: rm -f include/asm
rm -f include/asm命令会强制删除名为include/asm的文件或符号链接(如果不存在,则不会报错)。
( cd include ; ln -sf asm-$(ARCH) asm)它的作用是:cd include:切换到include目录。ln -sf asm-$(ARCH) asm:创建一个符号链接asm,指向asm-$(ARCH)。这里$(ARCH)是一个变量,通常代表当前的体系结构,如x86、arm等。-s表示创建符号链接,-f表示如果目标文件已存在,则强制覆盖。
@if [ ! -d include/linux/modules ]; then \ mkdir include/linux/modules; \ fi
这行命令检查include/linux/modules目录是否存在。如果不存在,则使用mkdir命令创建这个目录。@通常用在makefile中,表示这行命令在执行时不会回显到标准输出。
总结:这段脚本主要用于处理一些特定的符号链接和目录的创建,特别是在编译Linux内核或驱动模块时可能会用到。
其中涉及删除旧的符号链接(如果启用),创建新的指向特定体系结构的符号链接,以及确保特定目录存在。如果你在编写或修改makefile或shell脚本,确保根据你的具体需求调整这些命令,并正确设置环境变量如$(ARCH)。
oldconfig: symlinks
$(CONFIG_SHELL) scripts/Configure -d arch/$(ARCH)/config.in
通过命令界面配置内核,但是会自动载入既有的.config配置文件,并且只有在遇到先前没有设定过的选项时,才会要求你手动设定。然而,make config却会要求你手动设定所有的选项,即使你之前曾设定过。开发者通常会通过此方法将他们的配置更新为官方配置选项所做的变更,以避免重新设定整个内核的配置。
oldconfig::这是makefile中的一个目标名。当您在命令行中运行make oldconfig时,make将尝试构建或更新这个目标。
symlinks:这是makefile中的另一个目标,这里作为oldconfig的一个前置条件(prerequisite),那么make oldconfig将首先确保symlinks目标被构建(即执行完symlinks目标下的所有命令),它应该在这行之前定义,并且负责创建必要的符号链接。
$(CONFIG_SHELL):这是一个变量,通常用于指定要使用的shell程序。例如,它可能被设置为bash、sh或其他兼容的shell。
scripts/Configure:这是一个脚本的路径,很可能用于配置内核或某个组件。Configure脚本通常包含用于检测系统环境、设置构建选项和生成配置文件的逻辑。
-d arch/$(ARCH)/config.in:这是传递给Configure脚本的参数。-d可能是一个选项,用于指定配置文件的目录或文件。arch/$(ARCH)/config.in指向特定体系结构(由$(ARCH)变量指定)的配置输入文件。
综上所述,这行makefile代码的目的是定义一个名为oldconfig的目标,该目标依赖于symlinks(如果symlinks是一个目标的话),并使用$(CONFIG_SHELL)来执行scripts/Configure脚本,同时传递-d arch/$(ARCH)/config.in作为参数。
xconfig: symlinks
$(MAKE) -C scripts kconfig.tk
wish -f scripts/kconfig.tk
symlinks:这是一个前置条件或目标,表明在执行后面的命令之前需要确保某些符号链接(symlinks)已经正确设置。不过,在标准的Makefile中,我们通常不会直接看到这样的单词除非它作为目标或变量被定义。这里symlinks是上下文相关的一个特定目标,前面已经定义。
$(MAKE) -C scripts kconfig.tk:这是一个Make命令,它告诉make工具进入scripts目录(使用-C scripts),并在那里寻找或执行一个名为kconfig.tk的目标。这通常意味着在scripts目录下有一个Makefile或类似的构建脚本,其中定义了如何生成kconfig.tk。
wish -f scripts/kconfig.tk:这个命令看起来是调用wish程序(通常是Tcl/Tk的一部分,用于执行Tcl脚本),并使用-f选项来指定脚本文件,即scripts/kconfig.tk。这表明kconfig.tk是一个Tcl脚本,可能用于配置界面的生成或类似任务。
综上所述,这行代码的作用可能是在构建过程中,首先确保某些符号链接被正确设置(如果symlinks是一个目标的话),然后使用make工具在scripts目录下生成或更新kconfig.tk文件,最后使用wish程序执行这个Tcl脚本,可能用于展示一个图形化的配置界面。
menuconfig: include/linux/version.h symlinks
$(MAKE) -C scripts/lxdialog all
$(CONFIG_SHELL) scripts/Menuconfig arch/$(ARCH)/config.in
显示以curses为基础的终端机形式的配置菜单。如果.config文件存在,它会根据该文件来设定缺省值,如同make config.个人认为,make menuconfig 就是make oldconfig的图形版本。在将新的设定更新到.config中去的同时,将原来的.config文件保存为.config.old。
menuconfig 显然是一个目标,通常出现在 Makefile 或类似的构建脚本中。这个目标通常与配置 Linux 内核或其他类似的大型软件项目时的菜单配置界面相关。让我们逐步解析这个命令:
menuconfig:
这是目标名称,表示要生成或执行与菜单配置相关的某些操作。
include/linux/version.h:
这是一个前置条件,表示在执行后续命令之前,需要确保 include/linux/version.h 这个头文件存在或已经是最新的。这个文件通常包含项目的版本信息。
symlinks:
同样,这也是一个前置条件或目标,表示需要确保某些符号链接(symlinks)已经正确设置。在内核配置和构建过程中,符号链接经常用于指向正确的头文件、库文件或工具链。
$(MAKE) -C scripts/lxdialog all:
这是一个 Make 命令,告诉 make 工具进入 scripts/lxdialog 目录,并在那里执行 all 目标。lxdialog 是一个用于显示 Linux 内核配置菜单的工具,因此这个命令可能是用于构建或更新 lxdialog 工具。
$(CONFIG_SHELL) scripts/Menuconfig:
这里,$(CONFIG_SHELL) 是一个变量,通常指向用于配置脚本的解释器(如 bash、sh 等)。scripts/Menuconfig 是一个脚本文件,用于启动菜单配置界面。注意,这里的 Menuconfig 可能是大小写敏感的,具体取决于文件和目录的实际命名。
arch/$(ARCH)/config.in:
最后,这个部分指定了与特定体系结构(由 $(ARCH) 变量表示)相关的配置文件。这个文件包含了与体系结构相关的配置选项和默认值。
综上所述,这行代码的作用是:用图像化界面来配置linux内核。
config: symlinks
$(CONFIG_SHELL) scripts/Configure arch/$(ARCH)/config.in
是linux内核有问必答的配置方式,每个内核选项他都会问你要还是不要,即使是用户不关心的选项,选错了一个都必须从头在来一遍,用起来不方便。
include/config/MARKER: scripts/split-include include/linux/autoconf.h
scripts/split-include include/linux/autoconf.h include/config
@ touch include/config/MARKER
这个命令使用 touch 工具来更新或创建 include/config/MARKER 文件。touch 命令通常用于更新文件的时间戳,或者在不存在的情况下创建一个空文件。在这里,它可能是用来标记配置过程已经完成,或者作为后续构建步骤的一个信号。
linuxsubdirs: $(patsubst %, _dir_%, $(SUBDIRS))
在Makefile中,linuxsubdirs 这一行代码定义了一个变量,该变量包含了对 $(SUBDIRS) 变量中每个元素进行模式替换后得到的结果。让我们逐步解析这一行代码:
变量定义:
linuxsubdirs:这是正在定义的变量名。
函数使用:
$(patsubst %, _dir_%, $(SUBDIRS)):这里使用了Makefile的patsubst函数。
patsubst函数用于模式替换,它的语法是$(patsubst pattern, replacement, text)。
在这个例子中,pattern是%,表示一个通配符,匹配任何字符串。
replacement是_dir_%,表示在匹配的字符串前加上_dir_。
text是$(SUBDIRS),这是一个变量,通常包含了一个或多个子目录的名称。
替换过程:
对于$(SUBDIRS)中的每个目录名(假设它们是dir1、dir2、dir3...),patsubst函数会将它们替换为_dir_dir1、_dir_dir2、_dir_dir3...。
结果:最终,linuxsubdirs变量将包含一个由_dir_前缀和$(SUBDIRS)中每个目录名组成的列表。例如,如果$(SUBDIRS)被定义为foo bar baz,那么linuxsubdirs将会被展开为_dir_foo _dir_bar _dir_baz。在Linux内核或其他大型项目的Makefile中,这样的变量通常用于构建过程中,可能用于指定要编译或处理的子目录列表,或者用于其他需要区分或处理不同子目录的场合。
$(patsubst %, _dir_%, $(SUBDIRS)) : dummy include/linux/version.h include/config/MARKER
$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C $(patsubst _dir_%, %, $@)
在代码片段中,我们看到一个模式规则(pattern rule),它定义了如何生成一系列以_dir_为前缀的目标。这些目标依赖于几个文件,并且对于每个目标,都会执行一个特定的命令。让我们详细解析这个规则:
目标(targets)
$(patsubst %, _dir_%, $(SUBDIRS))
这是一个模式替换函数,它将$(SUBDIRS)变量中的每个元素(假设是一系列子目录名)都替换为以_dir_为前缀的新字符串。例如,如果$(SUBDIRS)包含foo bar,则这个函数将生成_dir_foo _dir_bar作为目标。
依赖(prerequisites)
dummy include/linux/version.h include/config/MARKER
这些是生成目标所依赖的文件。dummy可能是一个伪目标,用于强制执行命令(即使它本身不产生任何输出)。include/linux/version.h和include/config/MARKER可能是头文件或标记文件,用于表示某些配置或版本信息。
命令(commands)
$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C $(patsubst _dir_%, %, $@)
这是当目标需要更新时将要执行的命令。
$(MAKE):调用make工具。
CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)":设置编译器标志,结合了$(CFLAGS)和$(CFLAGS_KERNEL)变量的值。
-C $(patsubst _dir_%, %, $@):告诉make进入由$(patsubst _dir_%, %, $@)得到的目录。这里,patsubst函数再次被使用,但这次是为了从目标名(如_dir_foo)中去除_dir_前缀,得到原始的子目录名(如foo)。
解析
目标生成:对于$(SUBDIRS)中的每个目录,都会生成一个以_dir_为前缀的目标。
依赖检查:在生成这些目标之前,make会检查dummy、include/linux/version.h和include/config/MARKER是否存在且是最新的。
命令执行:如果依赖文件比目标文件更新或者目标文件不存在,make将进入对应的子目录(通过去除目标名中的_dir_前缀得到),并在那里执行make命令,同时传递指定的CFLAGS。
上下文
这个规则通常出现在构建大型项目(如Linux内核)的Makefile中,用于确保在每个子目录中都进行了必要的编译或配置步骤。
$(TOPDIR)/include/linux/version.h: include/linux/version.h
$(TOPDIR)/include/linux/compile.h: include/linux/compile.h
解析
目标文件的位置:这两个规则都表明,目标文件位于项目的顶层目录下(由$(TOPDIR)指定),而源文件则位于一个相对路径下(可能是项目的子目录)。
依赖关系:每个目标文件都依赖于一个同名的源文件。如果源文件被更新,那么目标文件也需要被更新(通过复制、编译或其他方式)。
隐式命令:由于规则中没有显示给出命令,make将使用默认的动作或隐式规则(通常是复制源文件到目标位置,或者如果目标文件已经存在并且是最新的,则什么也不做)。在实际的项目中,这些动作可能是通过Makefile中的其他部分定义的,或者是通过make的内置规则处理的。
构建流程:这些规则可能是项目构建流程的一部分,用于确保头文件在项目的不同部分之间保持一致。
newversion:
. scripts/mkversion > .tmpversion
@mv -f .tmpversion .version
在代码片段中,我们看到一个shell命令序列,它用于生成一个新的版本号文件,并将其保存为.version(.表示隐藏文件)。
scripts/mkversion:
这是一个shell命令,它执行scripts/mkversion脚本。这个脚本可能是项目的一部分,用于生成或更新版本号。通常,这样的脚本会读取当前的版本号,然后根据某些规则(如增加补丁号、次版本号或主版本号)来生成一个新的版本号。
> .tmpversion:
这是一个重定向操作,它将scripts/mkversion脚本的输出保存到.tmpversion文件中。这样做是为了避免直接覆盖现有的.version文件,以防在生成新版本号时发生错误。
@mv -f .tmpversion .version:
这是一个移动命令,它使用-f(force)选项来强制覆盖现有的.version文件(如果存在的话)与.tmpversion文件的内容。@符号通常用在makefile中,表示这是一个命令行命令,但在普通的shell脚本中,它通常不是必需的,除非它是作为makefile的一部分被解析。
综合起来,这个命令序列的作用是:执行scripts/mkversion脚本来生成一个新的版本号。将生成的版本号保存到.tmpversion文件中。强制将.tmpversion文件的内容移动到.version文件中,从而更新项目的版本号。
uts_len := 64
uts_truncate := sed -e 's/\(.\{1,$(uts_len)\}\).*/\1/'
在代码片段中,我们看到了两个变量定义和一个shell命令。这些代码可能是用于处理字符串的,特别是在截断字符串到特定长度时。让我们逐一解析它们:
变量定义:
uts_len := 64:
这是一个make变量定义,它将uts_len的值设置为64。这意味着后续可以使用$(uts_len)来引用这个值。
uts_truncate := sed -e 's/\(.\{1,$(uts_len)\}\).*/\1/':
这也是一个make变量定义,它将uts_truncate的值设置为一个shell命令。这个命令使用sed(流编辑器)来截断字符串。
sed -e 's/\(.\{1,$(uts_len)\}\).*/\1/':
-e 选项告诉sed执行后面的表达式。
s/\(.\{1,$(uts_len)\}\).*/\1/ 是sed的替换命令。它匹配从字符串开始的前$(uts_len)(即64)个字符,并替换整个字符串为这部分匹配的内容。换句话说,它会截断字符串到前64个字符。
\(.\{1,$(uts_len)\}\) 匹配前64个字符,并使用括号捕获它们。
.* 匹配剩余的字符串。
\1 替换为第一个捕获组(即前64个字符)。
用途:
这些变量可能在makefile中用于处理或生成文件时截断某些字符串。例如,在构建过程中,可能需要从某个长字符串中提取前64个字符,并将结果用于文件名、日志条目或其他目的。
使用示例:
在makefile中,您可能会看到类似这样的用法:
some_target:
@echo "Some long string" | $(uts_truncate) > truncated_output.txt
这将创建一个名为truncated_output.txt的文件,其中包含“Some long string”的前64个字符。
include/linux/compile.h: $(CONFIGURATION) include/linux/version.h newversion
@echo -n \#`cat .version` > .ver1
@if [ -n "$(CONFIG_SMP)" ] ; then echo -n " SMP" >> .ver1; fi
@if [ -f .name ]; then echo -n \-`cat .name` >> .ver1; fi
@LANG=C echo ' '`date` >> .ver1
@echo \#define UTS_VERSION \"`cat .ver1 | $(uts_truncate)`\" > .ver
@LANG=C echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> .ver
@echo \#define LINUX_COMPILE_BY \"`whoami`\" >> .ver
@echo \#define LINUX_COMPILE_HOST \"`hostname | $(uts_truncate)`\" >> .ver
@([ -x /bin/dnsdomainname ] && /bin/dnsdomainname > .ver1) || \
([ -x /bin/domainname ] && /bin/domainname > .ver1) || \
echo > .ver1
@echo \#define LINUX_COMPILE_DOMAIN \"`cat .ver1 | $(uts_truncate)`\" >> .ver
@echo \#define LINUX_COMPILER \"`$(CC) $(CFLAGS) -v 2>&1 | tail -1`\" >> .ver
@mv -f .ver $@
@rm -f .ver1
在代码片段中,我们看到了一个用于生成编译时头文件的完整makefile规则。这个规则的目标是生成一个包含编译时信息的头文件include/linux/compile.h。下面是该规则的逐步解析:
目标文件:
include/linux/compile.h:这是将要生成的头文件。
依赖:
$(CONFIGURATION):这是一个makefile变量,通常指向配置文件或配置选项。
include/linux/version.h:这是另一个头文件,可能包含版本信息。
newversion:这可能是一个目标或命令,用于生成或更新.version文件(尽管在提供的代码片段中没有直接显示其定义)。
命令序列:
@echo -n \#cat .version > .ver1:
这条命令读取.version文件中的内容,并在其前面加上#号,然后将结果保存到.ver1文件中。
检查并附加SMP、.name等信息到.ver1:
如果$(CONFIG_SMP)变量非空,则添加" SMP"到.ver1。
如果.name文件存在,则将其内容添加到.ver1(前面带有一个破折号)。
@LANG=C echo ' 'date >> .ver1:
这条命令将当前日期和时间(由date命令生成)添加到.ver1文件的末尾。
@echo \#define UTS_VERSION \"cat .ver1 | $(uts_truncate)\" > .ver:
这条命令读取.ver1文件的内容,通过$(uts_truncate)命令(可能是一个变量,代表一个用于截断字符串的命令)截断到特定长度,然后将其格式化为一个C语言的预处理指令,并保存到.ver文件中。
添加编译时间、编译者、编译主机等信息到.ver:
使用date +%T获取当前时间,whoami获取当前用户,hostname获取主机名,并通过$(uts_truncate)截断主机名。
尝试获取DNS域名(通过/bin/dnsdomainname或/bin/domainname),如果成功,则将其截断并添加到.ver文件中。
@echo \#define LINUX_COMPILER \"`$(CC) $(CFLAGS) -v 2>&1 | tail -1`\" >> .ver
这条命令尝试获取编译器的版本信息。它运行编译器(由$(CC)变量指定)带有-v选项(通常用于显示版本信息),然后从输出中提取最后一行,并将其格式化为一个C语言的预处理指令。
@mv -f .ver $@:
这条命令将.ver文件重命名为目标文件include/linux/compile.h。$@是一个makefile的自动变量,代表当前规则的目标。
@rm -f .ver1:
最后,删除临时文件.ver1。
总结:
这个makefile规则通过一系列命令生成了一个包含编译时信息的头文件。这些信息包括版本号、编译日期和时间、编译者、编译主机、编译域名以及编译器版本。这个头文件可能用于在编译时向内核或其他程序提供有关编译环境的信息。
include/linux/version.h: ./Makefile
@expr length "$(KERNELRELEASE)" \<= $(uts_len) > /dev/null || \
(echo KERNELRELEASE \"$(KERNELRELEASE)\" exceeds $(uts_len) characters >&2; false)
@echo \#define UTS_RELEASE \"$(KERNELRELEASE)\" > .ver
@echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)` >> .ver
@echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))' >>.ver
@mv -f .ver $@
在Makefile代码片段中,我们看到了一系列命令,它们用于生成include/linux/version.h头文件。这个头文件通常包含有关Linux内核版本的宏定义。让我们逐步解析这些命令:
检查KERNELRELEASE长度:
@expr length "$(KERNELRELEASE)" \<= $(uts_len) > /dev/null || \
(echo KERNELRELEASE "$(KERNELRELEASE)" exceeds $(uts_len) characters >&2; false)
这行代码使用expr命令来计算KERNELRELEASE变量的长度,并检查它是否小于或等于$(uts_len)(在前面的代码中定义为64)。如果KERNELRELEASE的长度超过$(uts_len),则会输出一条错误消息到标准错误输出(>&2),并且命令会返回失败状态(false)。如果长度检查通过,则不会有输出,因为成功的结果被重定向到/dev/null。
生成version.h文件的内容:
接下来的三个命令使用echo将宏定义写入一个临时文件.ver中。
@echo \#define UTS_RELEASE \"$(KERNELRELEASE)\" > .ver
@echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)` >> .ver
@echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))' >>.ver
第一个echo命令定义了UTS_RELEASE宏,其值为$(KERNELRELEASE)。
第二个echo命令定义了LINUX_VERSION_CODE宏,它是通过将主版本号($(VERSION))乘以65536,次版本号($(PATCHLEVEL))乘以256,再加上补丁号($(SUBLEVEL))来计算得到的。
第三个echo命令定义了KERNEL_VERSION宏,它是一个用于将版本号转换为整数的函数。
移动临时文件到目标位置:
@mv -f .ver $@
这行代码使用mv命令将.ver文件强制(-f选项)移动到目标文件include/linux/version.h的位置。$@是Makefile中的自动变量,代表当前规则的目标文件名。
综合起来,这段Makefile代码的作用是:检查KERNELRELEASE变量的长度是否不超过64个字符。如果长度检查通过,则生成一个包含内核版本信息的version.h头文件。最后,将生成的头文件移动到正确的位置。
comma := ,
在这段Makefile代码片段中,我们看到了一个简单的变量定义:
comma := ,
这里,comma 是一个变量名,而 := 是Makefile中用于立即赋值(也称为简单赋值)的操作符。,(逗号)是被赋给 comma 变量的值。这个定义的意思是,每当在Makefile的后续部分使用 $(comma) 时,它都会被替换为逗号 ,。
这样的变量定义在Makefile中很常见,特别是当您需要在多个地方重复使用相同的字符或字符串时。通过定义一个变量,您可以使Makefile更加清晰和可维护,因为您只需要在一个地方更新值,而不是在每个地方都硬编码它。例如,如果您在Makefile中需要构建一个由多个部分组成的列表,并且这些部分之间需要用逗号分隔,那么您就可以使用 $(comma) 来插入逗号,而不是每次都手动输入它。这样做可以减少输入错误,并使Makefile更容易阅读和理解。
init/version.o: init/version.c include/linux/compile.h include/config/MARKER
$(CC) $(CFLAGS) $(CFLAGS_KERNEL) -DUTS_MACHINE='"$(ARCH)"' -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o init/version.o init/version.c
这段代码是一个Makefile的编译指令,用于编译Linux内核中的一个源文件init/version.c。下面是对这段指令的详细解释:
目标文件:init/version.o
这是编译后生成的目标对象文件,位于init目录下。
源文件 :init/version.c
这是需要编译的源文件。
依赖文件:
include/linux/compile.h
include/config/MARKER
这些文件在编译init/version.c时被包含或引用,如果这些依赖文件有更新,那么init/version.c也需要重新编译。
编译器和编译选项:
$(CC):代表编译器,通常是gcc或类似的C编译器。
$(CFLAGS) 和 $(CFLAGS_KERNEL):这些是编译时使用的标志(flags),定义了编译过程中的一些参数,如优化级别、警告级别、包含路径等。
-DUTS_MACHINE='"$(ARCH)"':这是一个预处理定义,用于指定编译时的机器架构。$(ARCH)是一个变量,代表目标架构。
-DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))):这是一个复杂的预处理定义。$(subst ...)函数用于字符串替换,首先将$(*F)(即当前目标文件名的基本部分,不包括路径和扩展名)中的-替换为_,然后将,也替换为_。结果用作KBUILD_BASENAME的定义值。
编译指令:
-c:表示只编译源文件,而不进行链接。
-o init/version.o:指定输出文件的名称和路径。
综上所述,这条指令的目的是编译init/version.c文件,生成init/version.o对象文件,同时根据依赖关系和预处理定义来调整编译行为。这是Linux内核构建过程中常见的一部分,确保了代码的正确编译和构建。
init/main.o: init/main.c include/config/MARKER
$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o $@ $<
这段代码是Makefile中的一个编译指令,用于编译C语言源文件。让我们逐步解析这个指令:
目标文件:init/main.o
这是编译后生成的目标对象文件,位于init目录下,文件名为main.o。
源文件:init/main.c
这是要编译的C语言源文件,位于init目录下,文件名为main.c。
依赖文件:include/config/MARKER
编译main.c之前,需要确保include/config/MARKER文件存在或已更新。这通常是一个配置文件,用于定义编译时的某些参数或宏。
编译器和编译选项:
$(CC):这是一个变量,通常代表C编译器,如gcc。
$(CFLAGS) 和 $(CFLAGS_KERNEL):这两个变量包含了编译C文件时使用的标准编译选项和特定于内核的编译选项。
$(PROFILING):如果定义了剖析(profiling)相关的选项,这里会包含相应的编译参数,用于性能分析。
-DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))):这是一个预处理选项,用于定义宏KBUILD_BASENAME。$(*F)表示当前目标文件名(不包含路径和扩展名),这里用两个subst函数将文件名中的逗号和破折号替换为下划线。
编译指令:
-c:表示只编译源文件,而不进行链接。
-o $@:指定输出文件名,$@在这里代表目标文件名init/main.o。
$<:代表第一个依赖文件名,即init/main.c。
综上所述,这个Makefile指令的作用是:使用指定的编译器和编译选项,编译init/main.c文件,生成init/main.o对象文件,并且在编译过程中定义了一个与文件名相关的宏KBUILD_BASENAME。
init/do_mounts.o: init/do_mounts.c include/config/MARKER
$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o $@ $<
解释同上
fs lib mm ipc kernel drivers net: dummy
$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@)
这行代码看起来像是Makefile中的一个规则,用于构建某些目标。让我们逐步解析它的组成部分:
目标列表(fs lib mm ipc kernel drivers net):
这部分列出了多个目标,它们可能是目录名或者是将要构建的模块的名称。这些目标通常对应于Linux内核中的不同子系统或组件,如文件系统(fs)、内存管理(mm)、进程间通信(ipc)、内核核心(kernel)、设备驱动(drivers)和网络(net)。
命令(dummy $(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@)):
dummy:dummy可能是一个伪目标或占位符,用于强制执行命令(即使它本身不产生任何输出)具体取决于Makefile的其他部分。
$(MAKE):这是递归调用make命令,通常用于在当前Makefile中调用另一个Makefile或在相同的Makefile中递归构建目标。
CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)":这部分设置了编译标志。$(CFLAGS)和$(CFLAGS_KERNEL)是变量,通常定义在Makefile或其包含的环境中。这些标志用于指定编译器如何编译代码(例如,优化级别、警告级别、包含目录等)。
$(subst $@, _dir_$@, $@):这是一个字符串替换函数。$@代表当前规则的目标名。这个函数将目标名中的$@替换为_dir_$@,基本上是在目标名前加上_dir_前缀。这可能是为了指向一个不同的目录或构建一个与目标名相关但有所不同的目标。
总结:这行代码在Makefile中定义了一个规则,用于递归地构建一系列目标(fs, lib, mm, ipc, kernel, drivers, net)。每个目标都使用相同的命令,该命令通过$(MAKE)递归调用make,传递特定的编译标志,并可能修改目标名(通过添加_dir_前缀)来指向不同的构建目标或路径。要了解这行代码的确切作用,还需要查看Makefile的其他部分,特别是dummy、CFLAGS、CFLAGS_KERNEL等变量的定义,以及这些目标(如fs, lib等)是如何在Makefile中被进一步处理的。
TAGS: dummy
{ find include/asm-${ARCH} -name '*.h' -print ; \
find include -type d \( -name "asm-*" -o -name config \) -prune -o -name '*.h' -print ; \
find $(SUBDIRS) init arch/${ARCH} -name '*.[chS]' ; } | grep -v SCCS | grep -v '\.svn' | etags -
这条命令是一个用于生成etags标签文件的复杂shell命令,适用于在Linux或类Unix系统上开发的C或C++项目。etags文件用于文本编辑器(比如Emacs)中,以便快速跳转到定义或声明。以下是该命令的详细解释:
命令目的:生成一个包含项目中所有头文件(.h)和源文件(.c, .ch, .S)的etags标签文件。
TAGS: dummy
TAGS 是生成的标签文件的名称。
dummy 是一个占位符,可能用于指示这是一个自动生成的标签文件,或者用于与其他标签文件区分开。在实际使用中,dummy 可能不会影响标签文件的内容,只是作为文件名的一部分,用于强制执行命令(即使它本身不产生任何输出)具体取决于Makefile的其他部分。
{ ... }
花括号内的内容是一个命令块,其中的命令会依次执行,并将输出合并到一个流中。
find include/asm-${ARCH} -name '*.h' -print
搜索 include/asm-${ARCH} 目录下所有以 .h 结尾的文件(头文件)。${ARCH} 是一个环境变量,代表当前系统的架构,如 x86、arm 等。
-print 是 find 命令的另一个选项,用于打印匹配的文件和目录的完整路径。这是 find 命令的默认行为,但显式指定它可以使命令更清晰。
find include -type d \( -name "asm-*" -o -name config \) -prune -o -name '*.h' -print:在include目录下查找所有.h文件,但排除名为asm-*和config的目录。
find $(SUBDIRS) init arch/${ARCH} -name '*.[chS]':在$(SUBDIRS)、init和arch/${ARCH}目录下查找所有以.c、.h或.S(汇编文件)结尾的文件。$(SUBDIRS)是一个变量,通常包含项目的子目录列表。
| grep -v SCCS:通过管道将前面的输出传递给grep,排除包含SCCS的行。SCCS是一种源代码控制系统,这里可能是为了排除版本控制系统的元数据。
| grep -v '\.svn':再次使用grep排除包含.svn的行,.svn是Subversion版本控制系统的元数据目录。
| etags -:将过滤后的文件列表传递给etags命令,生成标签文件。-表示从标准输入读取数据。
总结:
这条命令通过多个find命令搜集项目中的头文件和源文件,使用grep过滤掉与版本控制系统相关的条目,最后使用etags生成一个标签文件,便于在编辑器中快速导航代码。
注意:
命令中的${ARCH}和$(SUBDIRS)需要根据你的项目环境进行适当的设置。这条命令可能需要根据你具体的项目结构或使用的版本控制系统进行调整。例如,如果使用的是Git而不是Subversion,可能需要排除.git目录。
# Exuberant ctags works better with -I
tags: dummy
CTAGSF=`ctags --version | grep -i exuberant >/dev/null && echo "-I __initdata,__exitdata,EXPORT_SYMBOL,EXPORT_SYMBOL_NOVERS"`; \
ctags $$CTAGSF `find include/asm-$(ARCH) -name '*.h'` && \
find include -type d \( -name "asm-*" -o -name config \) -prune -o -name '*.h' -print | xargs ctags $$CTAGSF -a && \
find $(SUBDIRS) init -name '*.[ch]' | xargs ctags $$CTAGSF -a
该命令是一个用于生成ctags标签文件的shell脚本,该标签文件可用于文本编辑器(如Vim)中以便快速导航到代码中的特定符号。这个命令特别适用于包含Linux内核代码或类似代码的项目,这些代码中可能包含特殊的宏定义,如__initdata、__exitdata、EXPORT_SYMBOL和EXPORT_SYMBOL_NOVERS。
以下是该命令的详细解释:
tags: dummy
表示一个名为 tags 的目标,dummy是一个占位符,没有实际作用,表示强制执行后续的命令。
CTAGSF=ctags --version | grep -i exuberant >/dev/null && echo "-I __initdata,__exitdata,EXPORT_SYMBOL,EXPORT_SYMBOL_NOVERS"
这行代码检查ctags的版本是否包含"exuberant"(不区分大小写)。如果找到,则设置环境变量CTAGSF为"-I __initdata,__exitdata,EXPORT_SYMBOL,EXPORT_SYMBOL_NOVERS"。-I参数告诉ctags忽略(或在某些情况下特殊处理)后续列出的宏定义。&&是一个逻辑操作符,它只在前面的命令(grep)成功时才执行后面的命令(echo)。>/dev/null这个重定向操作将grep命令的标准输出发送到/dev/null,/dev/null是一个特殊的文件,用于丢弃任何发送到它的数据。这样做是为了抑制grep命令的输出,我们只关心grep命令的退出状态。
ctags $$CTAGSF find include/asm-$(ARCH) -name '*.h'
使用ctags和CTAGSF变量(如果设置了的话)来处理include/asm-$(ARCH)目录下的所有头文件。
$(ARCH)是一个变量,通常代表当前的体系结构(如x86、arm等)。
find include -type d ( -name "asm-" -o -name config ) -prune -o -name '.h' -print | xargs ctags $$CTAGSF -a
在include目录下查找所有.h文件,但排除名为asm-*和config的目录。使用xargs将找到的文件列表传递给ctags,并使用-a参数将结果追加到现有的标签文件中(如果文件已存在)。
find $(SUBDIRS) init -name '*.[ch]' | xargs ctags $$CTAGSF -a
在$(SUBDIRS)和init目录下查找所有以.c和.h结尾的文件。使用xargs将找到的文件列表传递给ctags,并使用-a参数将结果追加到标签文件中。
注意事项:
$(ARCH)和$(SUBDIRS)变量需要根据您的项目环境进行适当的设置。如果ctags的版本不是exuberant,CTAGSF变量将不会被设置,这可能会影响后续ctags命令的行为。在大型项目中,生成ctags标签文件可能需要一些时间。
确保您的编辑器配置为使用生成的标签文件。例如,在Vim中,您可能需要设置tags选项来指定标签文件的路径。
如果您的项目结构或使用的编译器与命令中的假设不符,您可能需要对命令进行相应的调整。
ifdef CONFIG_MODULES
ifdef CONFIG_MODVERSIONS
MODFLAGS += -DMODVERSIONS -include $(HPATH)/linux/modversions.h
endif
ifdef CONFIG_MODULES:
这个指令检查CONFIG_MODULES是否已定义。在Linux内核编译中,CONFIG_MODULES通常用于启用或禁用内核模块的编译。如果CONFIG_MODULES已定义(即启用了模块编译),则继续处理内部的指令。
ifdef CONFIG_MODVERSIONS:
这个指令在内层检查CONFIG_MODVERSIONS是否已定义。CONFIG_MODVERSIONS用于启用或禁用模块版本控制。如果CONFIG_MODVERSIONS也已定义(即启用了模块版本控制),则执行下一行的指令。
MODFLAGS += -DMODVERSIONS -include $(HPATH)/linux/modversions.h:
这行指令将-DMODVERSIONS和-include $(HPATH)/linux/modversions.h添加到MODFLAGS变量中。
-DMODVERSIONS是一个编译器标志,用于定义MODVERSIONS宏,这可能在代码中用于标识启用了版本控制的模块。-include $(HPATH)/linux/modversions.h指示编译器在编译时包含指定路径下的modversions.h头文件。这个文件通常包含与模块版本控制相关的信息。
endif和endif:
这些指令分别结束内层和外层的ifdef块。
总的来说,这段代码的作用是:如果启用了模块编译(CONFIG_MODULES)并且启用了模块版本控制(CONFIG_MODVERSIONS),则在编译模块时添加-DMODVERSIONS标志,并包含$(HPATH)/linux/modversions.h头文件。这有助于在编译时管理和跟踪模块的版本信息。
.PHONY: modules
modules: $(patsubst %, _mod_%, $(SUBDIRS))
在Makefile中,.PHONY 指令用于声明一些“伪”目标,这些目标不是实际存在的文件名,而是用于执行命令的标签。
.PHONY: modules
这行指令声明了一个伪目标 modules。这意味着,无论当前目录中是否存在名为 modules 的文件或目录,make modules 命令都会执行与 modules 目标相关联的命令。
modules: $(patsubst %, _mod_%, $(SUBDIRS))
这行定义了 modules 目标,它依赖于通过 $(patsubst ...) 函数进行模式替换生成的一系列目标。
$(patsubst %, _mod_%, $(SUBDIRS)):这个函数对 $(SUBDIRS) 变量中的每个元素应用模式替换。% 是通配符,代表 $(SUBDIRS) 中的每个元素。_mod_% 是替换模式,意味着每个元素都会被替换为以 _mod_ 开头,后跟原始元素的形式。例如,如果 $(SUBDIRS) 包含 dir1 dir2,那么替换后的结果将是 _mod_dir1 _mod_dir2。因此,modules 目标依赖于 _mod_dir1、_mod_dir2 等目标(具体取决于 $(SUBDIRS) 的内容)。
这段代码的效果是,当你运行 make modules 时,Makefile 会尝试构建每个由 $(SUBDIRS) 变量中的元素通过模式替换生成的目标(例如 _mod_dir1)。这些目标通常会在Makefile的其他部分定义,指定如何构建与每个子目录相对应的模块。
.PHONY: $(patsubst %, _mod_%, $(SUBDIRS))
$(patsubst %, _mod_%, $(SUBDIRS)) : include/linux/version.h include/config/MARKER
$(MAKE) -C $(patsubst _mod_%, %, $@) CFLAGS="$(CFLAGS) $(MODFLAGS)" MAKING_MODULES=1 modules
.PHONY: $(patsubst %, _mod_%, $(SUBDIRS))
这行指令声明了所有通过模式替换$(patsubst %, _mod_%, $(SUBDIRS))生成的目标为伪目标。
$(patsubst %, _mod_%, $(SUBDIRS)): include/linux/version.h include/config/MARKER
这行定义了一个目标模式,其中每个目标都是通过模式替换生成的(例如_mod_dir1、_mod_dir2等)。
这些目标都依赖于include/linux/version.h和include/config/MARKER文件。
命令部分
$(MAKE) -C $(patsubst _mod_%, %, $@) CFLAGS="$(CFLAGS) $(MODFLAGS)" MAKING_MODULES=1 modules
这行命令会在每个子目录(由$(patsubst _mod_%, %, $@)得出,即去掉_mod_前缀的部分)中执行make。
CFLAGS="$(CFLAGS) $(MODFLAGS)"会将当前的CFLAGS和MODFLAGS传递给子make进程。$(patsubst ...) 是一个模式替换函数,用于将字符串中的一部分替换为另一部分。 $(patsubst _mod_%, %, $@) 中,这个函数被用于从当前目标名($@)中去除特定的前缀(_mod_),以获取基础目录名或目标名。MAKING_MODULES=1是一个环境变量,可能用于在子make进程中标识正在构建模块。modules是传递给子make进程的目标名,这通常会在子目录的Makefile中定义。
注意:
include/linux/version.h和include/config/MARKER应该是存在的文件,它们可能用于提供构建模块所需的版本信息和配置标记。$(SUBDIRS)应该是一个包含子目录名称的变量,这些子目录中包含了要构建的模块。这段代码假设在每个子目录中都有一个Makefile,并且这个Makefile定义了modules目标。
.PHONY: modules_install
modules_install: _modinst_ $(patsubst %, _modinst_%, $(SUBDIRS)) _modinst_post
在Makefile中,.PHONY 指令用于声明伪目标,即那些不是实际文件名的目标。伪目标通常用于执行命令,而不是生成文件。
.PHONY: modules_install
这行指令声明了 modules_install 是一个伪目标。
modules_install: _modinst_ $(patsubst %, _modinst_%, $(SUBDIRS)) _modinst_post
这行定义了 modules_install 目标,它依赖于以下几个目标:
_modinst_:这可能是一个预先定义的步骤,用于安装模块之前的准备工作。
$(patsubst %, _modinst_%, $(SUBDIRS)):这是一个模式替换,它遍历 $(SUBDIRS) 变量中的每个元素,并在其前面加上 _modinst_ 前缀。例如,如果 $(SUBDIRS) 包含 dir1 dir2,那么替换后的结果将是 _modinst_dir1 _modinst_dir2。这些目标通常会在Makefile的其他部分或子目录的Makefile中定义,用于安装每个子目录中的模块。_modinst_post:这可能是一个后续步骤,用于在安装所有模块后执行一些清理或配置工作。当您运行 make modules_install 时,Makefile 将尝试构建所有列出的依赖目标(_modinst_,通过模式替换生成的目标,以及 _modinst_post)。每个目标都可能对应一条或多条命令,这些命令在Makefile的其他部分定义,并指定了如何执行安装步骤。请注意,这里并没有直接给出构建这些目标的命令,因为这些通常会在Makefile的其他地方或者是在子目录的Makefile中定义。这段代码只是设置了依赖关系,确保在构建 modules_install 目标时,所有相关的安装步骤都会被执行。此外,如果 _modinst_,_modinst_post 以及通过模式替换生成的目标(如 _modinst_dir1)没有在Makefile中明确定义,那么 make 将会报错,因为它找不到构建这些目标所需的规则。因此,请确保您的Makefile中包含了所有必要的目标定义和命令。
.PHONY: _modinst_
_modinst_:
@rm -rf $(MODLIB)/kernel
@rm -f $(MODLIB)/build
@mkdir -p $(MODLIB)/kernel
@ln -s $(TOPDIR) $(MODLIB)/build
这是一段Makefile的脚本代码,用于在构建或安装模块时执行特定的操作。Makefile是一种特殊的文件格式,用于告诉make程序如何编译和链接程序。这段代码定义了一个伪目标(phony target)_modinst_,以及该目标执行时应该运行的命令。下面是对这段代码的详细解释:
.PHONY: _modinst_:这行代码声明了一个伪目标_modinst_。伪目标通常不是文件名,而是用于执行命令的标签。通过将目标声明为伪目标,可以避免与文件系统中的实际文件名冲突。
_modinst_::这表示定义了一个目标_modinst_,冒号后面跟随的是当这个目标被触发时将要执行的命令。
@rm -rf $(MODLIB)/kernel:这是第一条命令,使用rm -rf命令删除$(MODLIB)/kernel目录及其下的所有文件和子目录。$(MODLIB)是一个变量,它应该在Makefile的其他地方被定义,代表模块库的路径。@符号的作用是使命令在执行时不打印到终端上,即静默执行。
@rm -f $(MODLIB)/build:这是第二条命令,使用rm -f命令删除$(MODLIB)/build文件(如果存在)。-f选项表示强制删除,即使文件不存在也不会报错。
@mkdir -p $(MODLIB)/kernel:这是第三条命令,使用mkdir -p命令创建$(MODLIB)/kernel目录。-p选项表示如果父目录不存在,则连同父目录一起创建。
@ln -s $(TOPDIR) $(MODLIB)/build:这是第四条命令,使用ln -s命令创建一个符号链接,将$(TOPDIR)链接到$(MODLIB)/build。$(TOPDIR)是另一个变量,应该在Makefile的其他地方定义,代表顶级目录的路径。符号链接类似于Windows中的快捷方式,指向实际的文件或目录。
总的来说,这段Makefile脚本的目的是在构建或安装模块时,清理旧的构建环境,创建一个新的构建目录,并设置一个符号链接指向顶级目录。这样做可以确保构建环境的干净和一致性,同时方便地在模块库和顶级目录之间导航。
# If System.map exists, run depmod. This deliberately does not have a
# dependency on System.map since that would run the dependency tree on
# vmlinux. This depmod is only for convenience to give the initial
# boot a modules.dep even before / is mounted read-write. However the
# boot script depmod is the master version.
这段注释解释了为什么在某些条件下(即System.map文件存在时)要运行depmod命令,以及这样做的目的和考虑。下面是对这段注释的详细解释:
如果System.map文件存在,则执行depmod命令。System.map是一个包含内核符号信息的文件,通常在内核编译后生成。它对于内核模块(如驱动程序)来说很重要,因为模块需要知道它们要调用的内核函数的地址。这里故意没有将depmod命令设置为依赖于System.map文件,因为这样做会导致整个依赖树以vmlinux(内核的未压缩可执行文件)为根。换句话说,如果depmod直接依赖于System.map,而System.map是由vmlinux生成的,那么每次vmlinux发生变化时,depmod都会被触发,这可能不是期望的行为,因为depmod主要用于处理模块依赖关系,而不是每次内核构建时都需要运行。这个depmod的执行仅仅是为了方便,在根文件系统(/)以读写模式挂载之前,提供一个初始的modules.dep文件。modules.dep文件包含了模块之间的依赖关系,这样在系统启动时,加载模块时可以按照正确的顺序进行。 但是,启动脚本中的depmod是主版本。这意味着在系统完全启动并挂载了根文件系统后,会再次运行depmod来生成或更新modules.dep文件,以确保它包含所有最新的模块依赖关系。这次运行是基于实际的System.map文件和当前系统上的所有模块进行的。
综上所述,这段注释解释了为什么在某些构建或安装过程中,即使不直接依赖于System.map文件,也要运行depmod命令,并且强调了启动脚本中的depmod是最终生成模块依赖关系的主版本。
ifeq "$(strip $(INSTALL_MOD_PATH))" ""
depmod_opts :=
else
depmod_opts := -b $(INSTALL_MOD_PATH) -r
endif
.PHONY: _modinst_post
_modinst_post: _modinst_post_pcmcia
if [ -r System.map ]; then $(DEPMOD) -ae -F System.map $(depmod_opts) $(KERNELRELEASE); fi
这段Makefile脚本代码是条件编译和伪目标的一个例子,用于在安装内核模块时根据条件执行depmod命令。下面是对这段代码的详细解释:
条件判断:这部分代码使用ifeq来判断变量INSTALL_MOD_PATH(经过strip函数去除前后空格)是否为空。
如果INSTALL_MOD_PATH为空,则depmod_opts变量被设置为空。
如果INSTALL_MOD_PATH不为空,则depmod_opts变量被设置为-b $(INSTALL_MOD_PATH) -r。这里-b选项指定了模块的基础安装路径,-r或--root选项告诉depmod在指定的根目录下工作,而不是默认的/。
伪目标定义:这行代码声明了一个伪目标_modinst_post,伪目标不是实际的文件名,而是用于执行命令的标签。
伪目标命令:这部分定义了伪目标_modinst_post要执行的命令。首先,它依赖于另一个目标_modinst_post_pcmcia(这可能是一个用于处理PCMCIA模块的特殊目标,但在这段代码中没有给出定义)。
接下来是一个if语句,检查System.map文件是否可读(-r选项)。如果可读,则执行depmod命令。$(DEPMOD)是一个变量,通常在Makefile的其他地方定义,指向depmod程序的路径。
-ae选项告诉depmod生成或更新所有模块的依赖关系,并输出到标准输出(而不是默认的文件)。
-F System.map指定了System.map文件的路径,这是depmod生成模块依赖关系时需要的。
$(depmod_opts)是之前根据INSTALL_MOD_PATH设置的选项。
$(KERNELRELEASE)是另一个变量,通常包含内核的版本号,用于告诉depmod正在为哪个内核版本生成依赖关系。
综上所述,这段Makefile脚本代码的目的是在内核模块安装后(特别是在_modinst_post_pcmcia目标执行后),如果System.map文件存在且可读,就根据INSTALL_MOD_PATH的设置(如果有的话)运行depmod命令来生成或更新模块依赖关系。这是内核构建和模块安装过程中的一个重要步骤,确保了模块能够正确地被加载和使用。
# Backwards compatibilty symlinks for people still using old versions
# of pcmcia-cs with hard coded pathnames on insmod. Remove
# _modinst_post_pcmcia for kernel 2.4.1.
这段注释提供了关于_modinst_post_pcmcia目标以及为什么它可能不再需要的信息。下面是这段注释的详细解释:
向后兼容性(Backwards compatibility):这是指为了确保旧软件或旧版本的软件能够在新环境或新版本中继续工作而做出的努力。在这个上下文中,它指的是为了那些仍然在使用旧版本pcmcia-cs(PCMCIA子系统的一个软件包)的用户,这些旧版本可能在insmod(插入模块)命令中硬编码了特定的路径名。
硬编码的路径名(hard coded pathnames):这意味着在软件的源代码或配置中直接写入了特定的文件或目录路径,而不是通过变量或配置文件来指定。这会导致软件在文件或目录结构发生变化时无法正常工作。
_modinst_post_pcmcia目标:根据之前的Makefile代码,这是一个伪目标,可能在模块安装后执行一些特定的操作,比如为PCMCIA模块创建符号链接(symlinks)。这些符号链接可能是为了兼容旧版本的pcmcia-cs,它们期望模块位于特定的路径。
移除_modinst_post_pcmcia:注释中提到,对于内核2.4.1版本及以后的版本,应该移除_modinst_post_pcmcia目标。这可能是因为新版本的内核或pcmcia-cs已经不再需要这些向后兼容的符号链接,或者因为它们已经以不同的方式处理了模块路径的问题。
综上所述,这段注释说明了_modinst_post_pcmcia目标的存在是为了提供向后兼容性,特别是对于那些使用旧版本pcmcia-cs并硬编码了模块路径的用户。然而,随着内核版本的更新和pcmcia-cs的发展,这种向后兼容性可能不再需要,因此建议在内核2.4.1及以后的版本中移除该目标。
.PHONY: _modinst_post_pcmcia
_modinst_post_pcmcia:
cd $(MODLIB); \
mkdir -p pcmcia; \
find kernel -path '*/pcmcia/*' -name '*.o' | xargs -i -r ln -sf ../{} pcmcia
.PHONY: $(patsubst %, _modinst_%, $(SUBDIRS))
$(patsubst %, _modinst_%, $(SUBDIRS)) :
$(MAKE) -C $(patsubst _modinst_%, %, $@) modules_install
这段Makefile代码定义了几个伪目标和规则,用于在内核模块安装过程中执行特定的操作。下面是对这段代码的详细解释:
定义伪目标:.PHONY: _modinst_post_pcmcia
这行代码声明了一个伪目标_modinst_post_pcmcia。伪目标不是实际的文件名,而是Makefile中用于执行命令的标签。
_modinst_post_pcmcia:
cd $(MODLIB); \
mkdir -p pcmcia; \
find kernel -path '*/pcmcia/*' -name '*.o' | xargs -i -r ln -sf ../{} pcmcia
这个规则定义了当_modinst_post_pcmcia目标被触发时要执行的命令。
cd $(MODLIB);:首先切换到$(MODLIB)目录,这个变量通常指向模块库的位置。
mkdir -p pcmcia;:在当前目录下创建一个名为pcmcia的目录,如果目录已经存在则不会报错。
find kernel -path '*/pcmcia/*' -name '*.o' | xargs -i -r ln -sf ../{} pcmcia:这条命令使用find工具在kernel目录下搜索所有路径中包含pcmcia/且文件名为*.o(即内核模块)的文件。然后,使用xargs和ln -sf命令为这些文件在pcmcia目录中创建符号链接。xargs 用于构建并执行命令行,它将从标准输入接收的数据作为参数传递给指定的命令。-i 或 --replace 选项告诉 xargs 用 {} 替换字符串来标记输入项的位置。在这个上下文中,{} 会被替换成 find 命令找到的每个文件名。-r 或 --no-run-if-empty 选项确保如果没有输入数据,xargs 不会执行后面的命令。{}是find命令的占位符,代表找到的每个文件名,../{}是相对于当前目录(即$(MODLIB))的文件路径。综合起来,这条命令的作用是:在 kernel 目录及其子目录中搜索所有路径包含 pcmcia/ 且文件名以 .o 结尾的文件,并在当前工作目录的 pcmcia 子目录中为这些文件创建符号链接。这样,即使原始文件位于不同的子目录中,也可以通过 pcmcia 目录中的符号链接来访问它们。
为子目录定义伪目标和规则:
.PHONY: $(patsubst %, _modinst_%, $(SUBDIRS))
$(patsubst %, _modinst_%, $(SUBDIRS)) :
$(MAKE) -C $(patsubst _modinst_%, %, $@) modules_install
$(patsubst %, _modinst_%, $(SUBDIRS)):这行代码使用patsubst函数将$(SUBDIRS)变量中的每个子目录名前缀加上_modinst_,生成一系列新的伪目标名。
$(patsubst %, _modinst_%, $(SUBDIRS)) ::这定义了上述生成的每个伪目标对应的规则。
$(MAKE) -C $(patsubst _modinst_%, %, $@) modules_install:$(patsubst pattern,replacement,text)是Makefile中的模式字符串替换函数。在这里,_modinst_%是模式,%是模式中的通配符,它匹配任意字符序列。
$@是Makefile中的自动变量,它代表当前规则中的目标名。因此,$(patsubst _modinst_%, %, $@)会将当前目标名中的_modinst_前缀去除,只留下原始的子目录名。当任何一个以_modinst_为前缀的伪目标被触发时,这条命令会被执行。它使用make命令递归地进入对应的子目录(通过去除_modinst_前缀来获取子目录名),并在那里执行modules_install目标。这通常意味着在子目录中安装模块。
综合起来,这条命令的作用是:对于每个以_modinst_为前缀的目标,去除前缀后得到子目录名,然后在该子目录中递归地执行make modules_install命令。这样,Makefile就可以在一个顶层目录中管理多个子目录的模块安装过程。
# modules disabled....
else
modules modules_install: dummy
@echo
@echo "The present kernel configuration has modules disabled."
@echo "Type 'make config' and enable loadable module support."
@echo "Then build a kernel with module support enabled."
@echo
@exit 1
endif
这里定义了两个目标:modules和modules_install,并且它们都被指定为依赖于一个名为dummy的目标。然而,在这个上下文中,dummy目标很可能并没有实际定义,它只是一个占位符或者用来表示这两个目标不依赖于任何具体的文件或目标,而是直接执行后面的命令。
当尝试构建modules或modules_install目标时,Makefile会执行后面列出的命令。这些命令主要是打印一些信息到标准输出,告诉用户当前的内核配置不支持模块,并指导用户如何启用模块支持。
具体命令解释如下:
@echo:打印一个空行。@符号表示在执行命令时不打印命令本身,只打印命令的输出。
@echo "The present kernel configuration has modules disabled.":打印一条消息,告知用户当前内核配置不支持模块。
@echo "Type 'make config' and enable loadable module support.":指导用户如何启用可加载模块支持。
@echo "Then build a kernel with module support enabled.":告诉用户在启用模块支持后需要重新构建内核。
@echo:再次打印一个空行。
@exit 1:退出Makefile的执行,并返回状态码1,表示出现错误或不可执行的情况。
这些命令的目的是在用户尝试在没有模块支持的情况下构建或安装模块时,提供一个清晰的错误信息,并指导用户如何解决问题。由于exit 1命令的存在,这会导致make过程终止,并且返回一个非零状态码,这通常表示构建过程中出现了错误。
clean: archclean
find . \( -name '*.[oas]' -o -name core -o -name '.*.flags' \) -type f -print \
| grep -v lxdialog/ | xargs rm -f
rm -f $(CLEAN_FILES)
rm -rf $(CLEAN_DIRS)
$(MAKE) -C Documentation/DocBook clean
在Makefile中,clean目标通常用于删除构建过程中生成的所有文件,以恢复到一个“干净”的状态,就像从未构建过一样。下面是对您提供的clean目标命令的详细解释:
依赖关系:clean目标依赖于archclean目标。这意味着在执行clean之前,Makefile会先尝试执行archclean目标(如果定义了的话)。archclean通常用于清理与特定体系结构相关的文件。
find命令:find .:从当前目录开始搜索。$ -name '*.[oas]' -o -name core -o -name '.*.flags' $:匹配文件名。这里使用了转义的括号\(和\)来分组条件,匹配以.o、.a、.s结尾的文件,名为core的文件,或者以.flags结尾的隐藏文件,-o(或)运算符,。
-type f:只匹配文件(不是目录)。
-print:打印匹配的文件名。
grep -v lxdialog/:通过管道|将find命令的输出传递给grep。-v选项表示反向匹配,即排除包含lxdialog/的行。
xargs rm -f:xargs:这个命令用于构建并执行命令行,它将从标准输入(stdin)接收到的数据作为参数传递给指定的命令。xargs 能够处理比直接调用命令时更大的参数列表,因为它会分批处理这些参数。通过管道将grep的输出传递给xargs。xargs构建并执行rm -f命令,删除所有匹配的文件。
rm -f $(CLEAN_FILES):
删除CLEAN_FILES变量中列出的所有文件。$(CLEAN_FILES)是一个Makefile变量,应该在Makefile的其他地方定义。
rm -rf $(CLEAN_DIRS):
删除CLEAN_DIRS变量中列出的所有目录及其内容。-r选项表示递归删除,-f选项表示强制删除。
$(MAKE) -C Documentation/DocBook clean:
在Documentation/DocBook目录中递归地执行make clean命令。这通常用于清理文档或手册页生成的文件。
整个clean目标的作用是删除构建过程中生成的各种文件(包括对象文件、库文件、核心转储文件、标志文件等),以及CLEAN_FILES和CLEAN_DIRS变量中指定的额外文件和目录。最后,它还会清理Documentation/DocBook目录中的文件。这是确保项目保持“干净”状态的一种常见做法,便于从头开始重新构建项目。
mrproper: clean archmrproper
find . \( -size 0 -o -name .depend \) -type f -print | xargs rm -f
rm -f $(MRPROPER_FILES)
rm -rf $(MRPROPER_DIRS)
$(MAKE) -C Documentation/DocBook mrproper
在Makefile中,mrproper目标通常用于执行比clean目标更彻底的清理操作,删除所有生成的文件、配置文件、依赖文件等,以恢复到项目的初始状态。您提供的mrproper目标命令包含了多个步骤,下面是对这些步骤的详细解释:
依赖关系:
mrproper目标依赖于clean和archmrproper目标。这意味着在执行mrproper之前,Makefile会先尝试执行clean和archmrproper目标(如果定义了的话)。archmrproper可能用于清理与特定体系结构相关的额外文件。
find命令:
find .:从当前目录开始搜索。
$ -size 0 -o -name .depend $:匹配条件。这里使用了转义的括号$和$来分组条件,匹配大小为0的文件或者名为.depend的文件。
-type f:只匹配文件(不是目录)。
-print:打印匹配的文件名。
| xargs rm -f:通过管道将find命令的输出传递给xargs,然后xargs构建并执行rm -f命令来删除这些文件。
rm -f $(MRPROPER_FILES):
删除MRPROPER_FILES变量中列出的所有文件。$(MRPROPER_FILES)是一个Makefile变量,应该在Makefile的其他地方定义,包含需要被mrproper目标删除的文件列表。
rm -rf $(MRPROPER_DIRS):
删除MRPROPER_DIRS变量中列出的所有目录及其内容。-r选项表示递归删除,-f选项表示强制删除。
$(MAKE) -C Documentation/DocBook mrproper:
在Documentation/DocBook目录中递归地执行make mrproper命令。这通常用于彻底清理文档或手册页生成的文件和目录。
整个mrproper目标的作用是执行一个更彻底的清理操作,删除所有构建过程中生成的文件、依赖文件、配置文件等,以及MRPROPER_FILES和MRPROPER_DIRS变量中指定的额外文件和目录。最后,它还会清理Documentation/DocBook目录中的文件和目录。这是确保项目完全恢复到初始状态的一种做法,通常用于在切换项目版本或重新配置项目之前执行。
distclean: mrproper
rm -f core `find . \( -not -type d \) -and \
\( -name '*.orig' -o -name '*.rej' -o -name '*~' \
-o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \
-o -name '.*.rej' -o -name '.SUMS' -o -size 0 \) -type f -print` TAGS tags
在Makefile中,distclean目标通常用于执行比mrproper更加彻底的清理操作,以确保项目可以被重新分发或从一个干净的状态开始构建。您提供的distclean目标命令包含了多个步骤,其中一些步骤与mrproper相似,但增加了额外的文件删除操作。下面是对这些步骤的详细解释:
依赖关系:
distclean目标依赖于mrproper目标。这意味着在执行distclean之前,Makefile会先执行mrproper目标,执行更基本的清理操作。
删除core文件:
rm -f core:删除名为core的文件。core文件通常是程序崩溃时生成的转储文件,不需要在分发版本中包含。
使用find命令删除特定文件:
这部分命令使用find来搜索并删除符合特定模式的文件。命令被包裹在反引号(`)中,这意味着shell会先执行find命令,然后将输出作为rm -f命令的参数。
-not -type d:排除目录,只匹配文件。
-name '*.orig'、-name '*.rej'等:匹配具有特定扩展名的文件,这些文件通常是版本控制系统或编辑器生成的备份或冲突文件。
-name '*~':匹配以波浪号(~)结尾的文件,这些通常是编辑器的临时文件。
-name '#*#':匹配以井号(#)开头和结尾的文件,这些可能是编辑器的自动保存文件或临时文件。
-name '.*.orig'、-name '.*.rej':匹配以点(.)开头并跟随特定扩展名的隐藏文件。
-name '.SUMS':匹配名为.SUMS的文件,这些文件可能包含校验和信息。
-size 0:匹配大小为0的文件。
-type f:确保只匹配文件。
-print:打印匹配的文件名,这些文件名随后被rm -f命令删除。
删除TAGS和tags文件:
rm -f TAGS tags:删除名为TAGS和tags的文件。这些文件通常是由代码编辑器或工具生成的,用于快速跳转到代码中的定义或声明。
整个distclean目标的作用是执行一个比mrproper更加彻底的清理操作,删除所有构建过程中生成的文件、依赖文件、配置文件、备份文件、临时文件等,以及TAGS和tags文件。这是确保项目可以被重新分发或从一个完全干净的状态开始构建的一种做法。在执行distclean之后,项目应该恢复到初始状态,没有任何构建或编辑过程中生成的文件或目录。
backup: mrproper
cd .. && tar cf - linux/ | gzip -9 > backup.gz
sync
在Makefile中定义的backup目标通常用于在执行清理操作后创建项目的一个压缩备份。您提供的backup目标命令包含了多个步骤,这些步骤将依次执行以确保备份的创建。下面是对这些步骤的详细解释:
依赖关系:
backup目标依赖于mrproper目标。这意味着在执行backup之前,Makefile会先执行mrproper目标来清理项目。
更改目录并创建压缩包:
cd ..:首先,命令会更改到当前目录的父目录。这通常是为了从项目的根目录或特定位置开始打包。
tar cf - linux/:接下来,使用tar命令创建一个压缩包。c选项表示创建一个新的压缩包,f选项指定压缩包的文件名(在这里使用-表示将压缩包输出到标准输出)。linux/是要被打包的目录名,它应该是当前目录的子目录。
| gzip -9 > backup.gz:通过管道(|),将tar命令的输出传递给gzip命令进行压缩。-9选项表示使用最大级别的压缩。最后,使用> backup.gz将压缩后的数据重定向到名为backup.gz的文件中。
同步文件系统:
sync:最后,执行sync命令来同步文件系统。这会将所有修改过的缓冲区写入磁盘,确保备份文件完整地保存在磁盘上。
整个backup目标的作用是:
首先,通过执行mrproper目标来清理项目,删除所有不必要的文件和目录。
然后,更改到项目的父目录,并使用tar和gzip命令创建一个名为backup.gz的压缩包,其中包含linux/目录及其所有子目录和文件。
最后,执行sync命令来确保压缩包已经完整地写入磁盘。
这样,您就创建了一个项目的压缩备份,可以在需要时恢复或分发。请注意,在执行此目标之前,请确保您有足够的磁盘空间来存储备份文件,并且linux/目录包含您想要备份的所有内容。
sgmldocs:
chmod 755 $(TOPDIR)/scripts/docgen
chmod 755 $(TOPDIR)/scripts/gen-all-syms
chmod 755 $(TOPDIR)/scripts/kernel-doc
$(MAKE) -C $(TOPDIR)/Documentation/DocBook books
psdocs: sgmldocs
$(MAKE) -C Documentation/DocBook ps
pdfdocs: sgmldocs
$(MAKE) -C Documentation/DocBook pdf
htmldocs: sgmldocs
$(MAKE) -C Documentation/DocBook html
这里的代码段定义了几个目标(target),每个目标对应生成不同类型的文档。下面是对每个目标及其命令的解释:
sgmldocs:
chmod 755 $(TOPDIR)/scripts/docgen:将$(TOPDIR)/scripts/docgen脚本的权限设置为755,即所有者可读写执行,组用户和其他用户可读执行。
chmod 755 $(TOPDIR)/scripts/gen-all-syms:将$(TOPDIR)/scripts/gen-all-syms脚本的权限设置为755。
chmod 755 $(TOPDIR)/scripts/kernel-doc:将$(TOPDIR)/scripts/kernel-doc脚本的权限设置为755。
$(MAKE) -C $(TOPDIR)/Documentation/DocBook books:在$(TOPDIR)/Documentation/DocBook目录下执行make命令,目标是books,用于生成DocBook格式的文档。
psdocs:
依赖于sgmldocs目标,即先执行sgmldocs目标下的命令。
$(MAKE) -C Documentation/DocBook ps:在Documentation/DocBook目录下执行make命令,目标是ps,用于生成PostScript格式的文档。
pdfdocs:
依赖于sgmldocs目标。
$(MAKE) -C Documentation/DocBook pdf:在Documentation/DocBook目录下执行make命令,目标是pdf,用于生成PDF格式的文档。
htmldocs:
依赖于sgmldocs目标。
$(MAKE) -C Documentation/DocBook html:在Documentation/DocBook目录下执行make命令,目标是html,用于生成HTML格式的文档。
在这段Makefile代码中,$(TOPDIR)是一个变量,代表内核源代码的顶级目录。chmod 755命令用于改变文件的权限,使得这些脚本可以被执行。$(MAKE) -C是一个常用的make命令选项,-C后面跟的是要进入的目录,在这个目录中执行make命令。
简而言之,这段Makefile代码定义了如何生成Linux内核的不同格式的文档,包括DocBook格式、PostScript格式、PDF格式和HTML格式。
mandocs:
chmod 755 $(TOPDIR)/scripts/kernel-doc
chmod 755 $(TOPDIR)/scripts/split-man
$(MAKE) -C Documentation/DocBook man
chmod 755 $(TOPDIR)/scripts/kernel-doc:将$(TOPDIR)/scripts/kernel-doc脚本的权限设置为755,即所有者具有读、写和执行权限,组用户和其他用户具有读和执行权限。
chmod 755 $(TOPDIR)/scripts/split-man:将$(TOPDIR)/scripts/split-man脚本的权限也设置为755。
$(MAKE) -C Documentation/DocBook man:在Documentation/DocBook目录下执行make命令,目标是man,这通常用于生成手册页(man pages)。
这个mandocs目标通常用于生成Linux内核的手册页文档。它首先确保两个相关的脚本具有可执行权限,然后在指定的目录下执行make命令来生成手册页。
sums:
find . -type f -print | sort | xargs sum > .SUMS
find . -type f -print:在当前目录(.)及其子目录下查找所有类型为文件(-type f)的项,并打印它们的路径。
sort:对find命令输出的文件路径进行排序。
xargs sum:将排序后的文件路径作为参数传递给sum命令。sum命令计算并显示每个文件的校验和(通常是一个简单的校验和,如BSD校验和)。
> .SUMS:将xargs sum命令的输出重定向到.SUMS文件中。如果.SUMS文件已经存在,它将被覆盖。
这个sums目标用于生成当前目录及其子目录下所有文件的校验和,并将这些校验和保存到.SUMS文件中。这通常用于验证文件的完整性,例如在分发软件包或进行版本控制时。
dep-files: scripts/mkdep archdep include/linux/version.h
scripts/mkdep -- init/*.c > .depend
scripts/mkdep -- `find $(FINDHPATH) \( -name SCCS -o -name .svn \) -prune -o -follow -name \*.h ! -name modversions.h -print` > .hdepend
$(MAKE) $(patsubst %,_sfdep_%,$(SUBDIRS)) _FASTDEP_ALL_SUB_DIRS="$(SUBDIRS)"
这段Makefile代码定义了一个名为dep-files的目标,该目标用于生成依赖文件,这些文件通常用于记录源代码文件之间的依赖关系,以便在文件被修改时能够正确地重新编译受影响的文件。下面是该目标及其命令的详细解释:
依赖项:
scripts/mkdep:这是一个脚本,用于生成依赖信息。
archdep:这可能是一个目标或脚本,用于处理与特定架构相关的依赖。
include/linux/version.h:这是一个头文件,通常包含内核的版本信息。
生成.depend文件:
scripts/mkdep -- init/*.c > .depend:这条命令使用scripts/mkdep脚本处理init/*.c中的C源文件,生成依赖信息,并将这些信息输出到.depend文件中。.depend文件通常包含源文件和目标文件之间的依赖关系。--:这个参数在命令行中通常用来表示选项的结束和位置参数的开始。在这里,它可能是为了确保后续的参数(即文件列表)不会被解释为mkdep脚本的选项。
生成.hdepend文件:
scripts/mkdep -- `find $(FINDHPATH) \( -name SCCS -o -name .svn \) -prune -o -follow -name \*.h ! -name modversions.h -print` > .hdepend:它使用‘find‘命令在‘(FINDHPATH)指定的路径中查找所有头文件(*.h),但排除名为SCCS和.svn的目录以及名为modversions.h的文件。然后,它使用scripts/mkdep脚本处理这些头文件,生成依赖信息,并将这些信息输出到.hdepend`文件中。
为子目录生成依赖文件:
$(MAKE) $(patsubst %,_sfdep_%,$(SUBDIRS)) _FASTDEP_ALL_SUB_DIRS="$(SUBDIRS)":这条命令对$(SUBDIRS)变量中的每个子目录执行make命令。$(patsubst %,_sfdep_%,$(SUBDIRS))将$(SUBDIRS)中的每个目录名替换为以_sfdep_为前缀的形式,这可能是为了触发特定于子目录的依赖生成规则。_FASTDEP_ALL_SUB_DIRS="$(SUBDIRS)"可能是一个传递给子目录make调用的变量,指示它处理所有子目录的依赖。
总的来说,dep-files目标通过执行一系列命令来生成.depend和.hdepend文件,以及为子目录生成相应的依赖文件。这些依赖文件对于确保在源代码文件被修改时能够正确地重新编译整个项目是至关重要的。
ifdef CONFIG_MODVERSIONS
$(MAKE) update-modverfile
endif
ifdef CONFIG_MODVERSIONS
MODVERFILE := $(TOPDIR)/include/linux/modversions.h
else
MODVERFILE :=
endif
export MODVERFILE
在Makefile中,export关键字用于将变量导出到环境中,这样当Makefile执行递归调用(即一个make进程启动另一个make进程)时,这些导出的变量在子make进程中也是可见的。
当您写下export MODVERFILE时,您实际上是在告诉make:“请将MODVERFILE这个变量的值导出到环境中,以便在后续的make调用中可以使用它。”
这里是一个简单的例子来说明export的用法:
# 定义一个变量
MODVERFILE := some/path/to/modversions.h
# 导出这个变量
export MODVERFILE
# 一个目标,它可能会启动另一个make进程
some-target:
$(MAKE) -C some/subdirectory
在这个例子中,MODVERFILE变量被定义并导出。当some-target目标被执行,并且它启动了另一个make进程(通过$(MAKE) -C some/subdirectory)时,MODVERFILE变量的值将在那个子make进程中可用。请注意,export只影响那些在执行export语句之后启动的子make进程。如果MODVERFILE在导出之前已经被使用(例如在某个规则中),并且那个规则启动了子make进程,那么在那个子make进程中MODVERFILE将不会是导出的值。确保MODVERFILE变量在导出之前已经被正确定义,并且您确实需要在子make进程中访问它的值。如果不需要在子make进程中访问它,那么就不需要使用export。
depend dep: dep-files
checkconfig:
find * -name '*.[hcS]' -type f -print | sort | xargs $(PERL) -w scripts/checkconfig.pl
checkhelp:
find * -name [cC]onfig.in -print | sort | xargs $(PERL) -w scripts/checkhelp.pl
checkincludes:
find * -name '*.[hcS]' -type f -print | sort | xargs $(PERL) -w scripts/checkincludes.pl
在您提供的Makefile片段中,定义了几个目标(depend、checkconfig、checkhelp、checkincludes),每个目标都关联了一系列要执行的命令。这些命令通常用于检查或生成与内核或软件项目相关的配置、帮助文档或依赖关系。下面是对每个目标的简要说明:
depend目标:
依赖于dep-files(这可能是一个文件列表或是一个生成这些文件的规则)。
命令部分缺失,但通常这里会包含用于生成依赖关系的命令,比如调用编译器或特定的脚本来分析源代码并生成依赖文件。
checkconfig目标:
使用find命令查找当前目录及子目录下所有扩展名为.h、.c或.S的文件。
将找到的文件排序后,通过管道传递给xargs命令,后者使用Perl脚本scripts/checkconfig.pl来检查这些文件的配置。
-w选项通常用于Perl脚本,表示启用警告。
checkhelp目标:
使用find命令查找当前目录及子目录下所有名为Config.in的文件(不区分大小写)。
同样地,将找到的文件排序后通过管道传递给xargs和Perl脚本scripts/checkhelp.pl来检查帮助文档的完整性或格式。
checkincludes目标:
与checkconfig类似,使用find命令查找.h、.c或.S文件。
然后使用xargs和Perl脚本scripts/checkincludes.pl来检查这些文件中的包含(#include)指令,确保所有包含的文件都是可访问的,没有拼写错误或循环依赖等问题。
这些目标通常用于内核或大型软件项目的构建系统中,以确保代码的正确性、一致性和可维护性。每个目标都通过特定的脚本或工具来执行一系列的检查或生成操作,这些操作对于项目的构建和配置至关重要。请确保在执行这些目标之前,已经正确安装了所需的工具(如Perl)和脚本,并且dep-files(如果它是一个文件列表或模式)是正确配置的。此外,由于这些命令可能会递归地处理大量文件,因此在大型项目中执行时可能需要一些时间。
ifdef CONFIGURATION
..$(CONFIGURATION):
@echo
@echo "You have a bad or nonexistent" .$(CONFIGURATION) ": running 'make" $(CONFIGURATION)"'"
@echo
$(MAKE) $(CONFIGURATION)
@echo
@echo "Successful. Try re-making (ignore the error that follows)"
@echo
exit 1
#dummy: ..$(CONFIGURATION)
dummy:
else
dummy:
endif
如果CONFIGURATION变量已定义,则执行一系列echo命令,并尝试运行一个以CONFIGURATION变量值为目标的make命令。
如果CONFIGURATION变量未定义,则执行dummy:目标(这里似乎是一个占位符,没有实际命令)。
include Rules.make
在Makefile中,include Rules.make 这条指令的作用是包含(或导入)另一个名为 Rules.make 的Makefile文件。这种做法在大型项目或需要复用构建规则的场景中非常常见。
当你执行 make 命令时,它会首先读取当前Makefile中的指令,然后按照 include 指令去查找并包含 Rules.make 文件。Rules.make 文件中可以包含变量定义、规则(目标及其依赖关系)、函数等Makefile的常规内容。一旦 Rules.make 被包含进来,它就像是当前Makefile的一部分一样被处理。
这里有几个关于 include 指令的要点:
文件路径:Rules.make 文件应该位于make能够找到的路径上。如果它不在当前目录下,你需要提供相对路径或绝对路径。
递归包含:Rules.make 本身也可以包含其他的Makefile文件,这样可以实现复杂的构建系统的层次化组织。
变量和规则的覆盖:如果当前Makefile和 Rules.make 中定义了相同的变量或规则,那么后者会覆盖前者(取决于包含的顺序)。
错误处理:如果 Rules.make 文件不存在或无法读取,make通常会报错并停止执行。但是,你可以通过在某些版本的make中使用 -include 指令来代替 include,这样即使文件不存在也不会报错,只是简单地忽略它。
环境变量:Rules.make 文件中的构建规则可以依赖于环境变量,这些变量可以在命令行上设置,或者在包含 Rules.make 之前的Makefile文件中定义。
使用 include 指令可以极大地提高Makefile的可维护性和可重用性,使得构建系统的不同部分可以更容易地组织和管理。
#
# This generates dependencies for the .h files.
#
scripts/mkdep: scripts/mkdep.c
$(HOSTCC) $(HOSTCFLAGS) -o scripts/mkdep scripts/mkdep.c
scripts/split-include: scripts/split-include.c
$(HOSTCC) $(HOSTCFLAGS) -o scripts/split-include scripts/split-include.c
scripts/mkdep:和scripts/split-include:是同一个意思,这里只解释一个。在Makefile中,scripts/mkdep: scripts/mkdep.c $(HOSTCC) $(HOSTCFLAGS) -o scripts/mkdep scripts/mkdep.c 这行实际上定义了一个构建规则,用于生成名为 scripts/mkdep 的可执行文件。这个规则指定了如何从源文件 scripts/mkdep.c 编译生成目标文件 scripts/mkdep。
这里是对这行内容的详细解释:
scripts/mkdep:这是规则的目标(target),即你想要构建的文件。
scripts/mkdep.c:这是规则的依赖(dependency),即构建目标所需的源文件。
$(HOSTCC):这是一个Makefile变量,通常用于指定编译器。HOSTCC 可能被设置为交叉编译器或系统默认的C编译器,具体取决于项目的配置。
$(HOSTCFLAGS):这是另一个Makefile变量,包含编译源文件时所需的编译器标志(compiler flags)。这些标志可能包括优化级别、警告选项、包含目录等。
-o scripts/mkdep:这是编译器的输出选项,指定了编译后的可执行文件应该被命名为 scripts/mkdep。
scripts/mkdep.c:在命令的末尾再次列出源文件,这是告诉编译器要对哪个文件进行编译。在某些情况下,这个源文件列表可能会包含多个文件,但这里只有一个。
整个规则的意思是:使用 $(HOSTCC) 编译器和 $(HOSTCFLAGS) 编译器标志来编译 scripts/mkdep.c 源文件,并将生成的可执行文件命名为 scripts/mkdep。
在Makefile中,这样的规则通常用于编译项目的源代码。当你运行 make 命令时,make会查找Makefile中的规则,并根据这些规则以及文件之间的依赖关系来决定需要构建哪些文件。如果 scripts/mkdep 文件不存在,或者它的依赖文件 scripts/mkdep.c 比它更新,那么make就会执行这条规则来构建 scripts/mkdep。
#
# RPM target
#
# If you do a make spec before packing the tarball you can rpm -ta it
#
spec:
. scripts/mkspec >kernel.spec
在Makefile中,注释通常以#字符开始,并且持续到行尾。所以,# # RPM target # #这部分是注释,用于说明接下来的内容或目标与RPM(Red Hat Package Manager)相关。
接下来的行:
spec: . scripts/mkspec >kernel.spec
定义了一个名为spec的目标,并且为这个目标指定了一个规则。这个规则看起来有些不寻常,因为它没有明确的依赖文件列表,而是使用了一个.(点)作为依赖,这通常不是Makefile的标准用法。不过,在这个上下文中,.可能是作为一个占位符或者是一个特殊的约定,其具体含义可能取决于该Makefile所处的项目或构建系统的特定规则。然而,更有可能的是,这里的.实际上是一个错误或者是一个误用,因为标准的Makefile规则通常是目标: 依赖的形式,其中依赖是构建目标所需的文件或条件。规则中的scripts/mkspec >kernel.spec部分看起来更像是一个shell命令,它的作用可能是执行scripts/mkspec脚本,并将输出重定向到kernel.spec文件中。这个命令可能是用于生成RPM包的规范文件(spec file),这是RPM包构建过程中所需的一个关键文件,它包含了包的元数据、构建指令、文件列表等信息。如果我们的目的是在Makefile中定义一个目标来生成这个spec文件,那么更标准的写法可能是这样的:
spec: scripts/mkspec
scripts/mkspec > kernel.spec
在这个修正后的版本中,spec是目标,scripts/mkspec是依赖(假设mkspec是一个脚本文件,且位于scripts目录下)。当make spec被调用时,make会检查scripts/mkspec是否存在且比spec目标更新(或者spec目标不存在),如果是,则执行后面的命令来生成kernel.spec文件。注意,这里我们没有使用.作为依赖,而是明确指定了scripts/mkspec作为依赖文件。
#
# Build a tar ball, generate an rpm from it and pack the result
# There arw two bits of magic here
# 1) The use of /. to avoid tar packing just the symlink
# 2) Removing the .dep files as they have source paths in them that
# will become invalid
#
rpm: clean spec
find . \( -size 0 -o -name .depend -o -name .hdepend \) -type f -print | xargs rm -f
set -e; \
cd $(TOPDIR)/.. ; \
ln -sf $(TOPDIR) $(KERNELPATH) ; \
tar -cvz --exclude CVS -f $(KERNELPATH).tar.gz $(KERNELPATH)/. ; \
rm $(KERNELPATH) ; \
cd $(TOPDIR) ; \
. scripts/mkversion > .version ; \
rpm -ta $(TOPDIR)/../$(KERNELPATH).tar.gz ; \
rm $(TOPDIR)/../$(KERNELPATH).tar.gz
这段Makefile脚本定义了一个名为rpm的目标,该目标执行了一系列命令来构建一个tar包,从该tar包生成一个RPM包,并清理生成过程中的临时文件。下面是对这段脚本的逐行解释和分析:
rpm: clean spec
这行定义了rpm目标,它依赖于clean和spec两个目标。这意味着在执行rpm目标之前,make会先执行clean和spec目标(如果它们还没有被执行的话)。
find . $ -size 0 -o -name .depend -o -name .hdepend $ -type f -print | xargs rm -f
这行命令使用find来查找当前目录(.)下所有大小为0的文件、名为.depend或.hdepend的文件,并将它们删除。xargs rm -f用于实际执行删除操作。
set -e;
这行命令告诉shell如果任何语句的执行结果不是true则退出。这是一种错误处理机制,用于确保脚本在遇到错误时不会继续执行。
cd $(TOPDIR)/.. ;
切换到$(TOPDIR)的父目录。$(TOPDIR)是一个Makefile变量,通常指向项目的顶级目录。
ln -sf $(TOPDIR) $(KERNELPATH) ;
创建一个符号链接,将$(TOPDIR)链接到$(KERNELPATH)。-s表示创建的是符号链接,-f表示如果链接已存在则强制覆盖。
tar -cvz --exclude CVS -f $(KERNELPATH).tar.gz $(KERNELPATH)/. ;
使用tar命令创建一个压缩的tar包,包含$(KERNELPATH)目录的内容,但排除名为CVS的目录。-c表示创建新包,-v表示显示详细信息,-z表示使用gzip压缩。-f指定输出文件的名称。注意这里的$(KERNELPATH)/.是为了避免只打包符号链接本身。
rm $(KERNELPATH) ;
删除之前创建的符号链接。
cd $(TOPDIR) ;
切换回项目的顶级目录。
. scripts/mkversion > .version ;
执行scripts/mkversion脚本,并将其输出重定向到.version文件中。这个脚本可能是用于生成或更新版本号信息的。
rpm -ta $(TOPDIR)/../$(KERNELPATH).tar.gz ;
使用rpm命令从之前创建的tar包中生成RPM包。-t表示测试构建,但实际上它会生成RPM包,-a表示构建所有二进制包。
rm $(TOPDIR)/../$(KERNELPATH).tar.gz
最后,删除之前创建的tar包,以清理工作区。
整个rpm目标执行了一个完整的构建流程:清理旧文件、生成spec文件、创建tar包、生成RPM包,并清理临时文件。这个流程是自动化构建和打包Linux内核或类似项目的典型步骤。