下面的代码来自linux2.6.30.4的源码文件中的顶层Makefile文件,黑体部分是对Makefile文件的分析
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 30
EXTRAVERSION = .4
NAME = Man-Eating Seals of Antiquity
/*上面定义了5个变量,通过uname -a命令可以看到;注意makefile中变量的定义等号两边可以有空格,而在shell中变量的定义赋值号两边不能有空格*/
# *DOCUMENTATION*
# To see a list of typical targets execute "make help"
# More info can be located in ./README
# Comments in this file are targeted only to the developer, do not
# expect to learn how to build the kernel reading this file.
/*上面这段英文意思是makefile文件中并没有说明怎么编译linux kernel,怎么编译linux kernel可以从readme文件中知道,可以运行make help来查看帮助*/
# Do not:
# o use make's built-in rules and variables
# (this increases performance and avoids hard-to-debug behaviour);
# o print "Entering directory ...";
MAKEFLAGS += -rR --no-print-directory
/*采用变量追加+=方式给变量在原有基础上添加选项,-r表示取消make的所有内嵌隐含规则,-R表示取消make所有的内嵌隐含变量,--no-print-directory取消make -C选项的默认打开的-w功能,即不打印出进入目录的提示信息,这种信息在调试的时候比较有用。*/
/*存在两个特殊的变量“ SHELL”和“ MAKEFLAGS”,对于这两个变量除非使用指示符“ unexport”对它们进行声明,它们在整个 make 的执行过程中始终被自动的传递给所有的子 make。*/
# We are using a recursive build, so we need to do a little thinking to get the ordering right. Most importantly: sub-Makefiles #should only ever modify files in their own directory. If in some directory we have a dependency on a file in another dir (which #doesn't happen often, but it's often unavoidable when linking the built-in.o targets which finally turn into vmlinux), we will call #a sub make in that other dir, and after that we are sure that everything which is in that other dir is now up to date.
# The only cases where we need to modify files which have global effects are thus separated out and done before the #recursive descending is started. They are now explicitly listed as the prepare rule.
# To put more focus on warnings, be less verbose as default Use 'make V=1' to see the full commands
/*上面英文的大概意思是说内核编译是一个递归的编译过程,在编译过程中一个目录下面的文件可能会依赖另一个目录下面的文件,这时就会调用另一个目录下面的makefile文件,来确保所依赖的文件是最新的。而要达到这个目的,就需要应用一些技巧来确保编译过程中在目录切换的过程中得到正确的命令*/
ifdef V
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif
/*make的origin函数,获取变量V的定义方式,V可以来自于make命令行,也可以使用makefile文件中的定义*/
# Call a source code checker (by default, "sparse") as part of the C compilation.
# Use 'make C=1' to enable checking of only re-compiled files.
# Use 'make C=2' to enable checking of *all* source files, regardless of whether they are re-compiled or not.
# See the file "Documentation/sparse.txt" for more details, including where to get the "sparse" utility.
ifdef C
ifeq ("$(origin C)", "command line")
KBUILD_CHECKSRC = $(C)
endif
endif
ifndef KBUILD_CHECKSRC
KBUILD_CHECKSRC = 0
endif
/*使用sparse工具进行内核代码静态检查,make C=1检查所有重新编译的代码,make C=2 检查所有代码, 不管是不是被重新编译;如果linux发行版不包括sparse工具则需要安装*/
# Use make M=dir to specify directory of external module to build
# Old syntax make ... SUBDIRS=$PWD is still supported
# Setting the environment variable KBUILD_EXTMOD take precedence
ifdef SUBDIRS
KBUILD_EXTMOD ?= $(SUBDIRS)
endif
ifdef M
ifeq ("$(origin M)", "command line")
KBUILD_EXTMOD := $(M)
endif
endif
/*在make命令行使用M=dir或者SUBDIRS=$PWD来设置外部模块的编译目录,?=条件赋值,只有变量在没有赋值的情况下才能赋值, :=表示变量的赋值是立即展开,即在定义时就给变量赋值,而=方式是延时展开,即在使用的时候才展开变量*/
# kbuild supports saving output files in a separate directory.
# To locate output files in a separate directory two syntaxes are supported.
# In both cases the working directory must be the root of the kernel src.
# 1) O=
# Use "make O=dir/to/store/output/files/"
# 2) Set KBUILD_OUTPUT
# Set the environment variable KBUILD_OUTPUT to point to the directory where the output files shall be placed.
# export KBUILD_OUTPUT=dir/to/store/output/files/
# make
# The O= assignment takes precedence over the KBUILD_OUTPUT environment variable.
/*编译内核产生的输出文件可以保存在一个独立的目录中,通过两种方式可以设置这个独立的目录,但必须要求工作目录为内核源码的根目录。一种是使用make命令行的方式设置,另一种是设置环境变量,用export导出环境变量,用命令行的方式设置的输出目录先于环境变量设置的输出目录使用*/
# KBUILD_SRC is set on invocation of make in OBJ directory
# KBUILD_SRC is not intended to be used by the regular user (for now)
ifeq ($(KBUILD_SRC),)
/*如果变量KBUILD_SRC没有定义,则执行*/
# OK, Make called in directory where kernel src resides
# Do we want to locate output files in a separate directory?
ifdef O
ifeq ("$(origin O)", "command line")
KBUILD_OUTPUT := $(O)
endif
endif
/*如前面介绍,这里优先使用make命令行设置的输出文件目录,否则就使用环境变量定义的变量*/
# That's our default target when none is given on the command line
PHONY := _all
_all:
/*在命令行不带参数时的默认编译目标为_all*/
# Cancel implicit rules on top Makefile
$(CURDIR)/Makefile Makefile: ; /*操作查看当前目录Makefile是否为最新*/
/*这里使用空命令行来禁用makefile的隐含规则,空命令行可以防止make在执行时试图为重建这个目标去查找隐含命令,在 make 的递归调用中,需要了解一下变量“ CURDIR”,此变量代表 make 的工作目录。当使用“ -C”选项进入一个子目录后,此变量将被重新赋值。总之,如果在Makefile 中没有对此变量进行显式的赋值操作,那么它代表 make 的工作目录。*/
ifneq ($(KBUILD_OUTPUT),)
# Invoke a second make in the output directory, passing relevant variables
# check that the output directory actually exists
saved-output := $(KBUILD_OUTPUT)
KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd)
$(if $(KBUILD_OUTPUT),, \
$(error output directory "$(saved-output)" does not exist))
/*如果定义了输出文件目录,就对这个目录进行测试,看这个目录是否真实存在,如果目录不存在则给出错误提示并退出make。这里使用了make的shell函数来在shell环境中执行cd命令,并使用make的error函数来输出错误提示*/
PHONY += $(MAKECMDGOALS) sub-make
/*make 在执行时设置一个特殊变量“ MAKECMDGOALS”,此变量记录了命令行参数指定的终极目标列表,没有通过参数指定终极目标时此变量为空。*/
$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make
$(Q)@:
/*使用make的filter-out反过滤函数将_all、sub-make、Makefile从目标列表中过滤出去,剩下的作为终极目标;$(Q)@:是规则的命令行,在这里是禁止回现,在命令前加@表示禁止回显,变量Q为空,:冒号在shell中为空命令*/
sub-make: FORCE
$(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \
KBUILD_SRC=$(CURDIR) \
KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile \
$(filter-out _all sub-make,$(MAKECMDGOALS))
/*FORCE在这里使得规则的命令总被执行,make的if函数用于给规则的命令前加@,表示是否回显;$(KBUILD_VERBOSE:1=)是变量的替换引用,表示将KBUILD_VERBOSE中的1替换为空后的值作为if函数的条件判断*/
# Leave processing to above invocation of make
skip-makefile := 1 /*只有设置了KBUILD_OUTPUT变量才会置这个标志*/
endif # ifneq ($(KBUILD_OUTPUT),)
endif # ifeq ($(KBUILD_SRC),)
# We process the rest of the Makefile if this is the final invocation of make
ifeq ($(skip-makefile),)
# If building an external module we do not care about the all: rule
# but instead _all depend on modules
PHONY += all
ifeq ($(KBUILD_EXTMOD),)
_all: all
else
_all: modules
endif
/*给变量PHONY追加all目标,如果没有定义KBUILD_EXTMOD,_all依赖于all,否则依赖于modules*/
srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))
TOPDIR := $(srctree)
# FIXME - TOPDIR is obsolete, use srctree/objtree
objtree := $(CURDIR)
src := $(srctree)
obj := $(objtree)
/*使用变量立即展开方式分别设置源码树目录,顶层目录,目标数目录,源文件目录、目标目录*/
VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
/*通过变量“ VPATH”可以指定makefile中所有文件(包括依赖文件和目标文件)的搜索路径,当规则的依赖文件在当前目录不存在时, make 会在此变量所指定的目录下去寻找这些依赖文件*/
export srctree objtree VPATH TOPDIR
/*export用于将上层makefile中定义的变量传递到子makefile中使用;上层 make 过程要将所执行的 Makefile 中的变量传递给子 make 过程,需要明确地指出。在 GNU make 中,实现此功能的指示符是“ export”。当一个变量使用“ export”进行声明后,变量和它的值将被加入到当前工作的环境变量中,以后在 make 执行的所有规则的命令都可以使用这个变量。而当没有使用指示符“ export”对任何变量进行声明的情况下,上层 make 只将那些已经初始化的环境变量(在执行 make 之前已经存在的环境变量)和使用命令行指定的变量(如命令“ makeCFLAGS +=-g”或者“ make –e CFLAGS +=-g”)传递给子 make 程序*/
# SUBARCH tells the usermode build what the underlying arch is. That is set first, and if a usermode build is happening, the #"ARCH=um" on the command line overrides the setting of ARCH below. If a native build is happening, then ARCH is #assigned, getting whatever value it gets normally, and SUBARCH is subsequently ignored.
SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
-e s/arm.*/arm/ -e s/sa110/arm/ \
-e s/s390x/s390/ -e s/parisc64/parisc/ \
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-e s/sh[234].*/sh/ )
/*使用make的shell函数,执行uname -m命令,命令的返回结果通过管道传递给sed函数进行模式替换后进行赋值*/
# Cross compiling and selecting different set of gcc/bin-utils When performing cross compilation for other architectures #ARCH shall be set to the target architecture. (See arch/* for the possibilities).
# ARCH can be set during invocation of make:
# make ARCH=ia64
# Another way is to have ARCH set in the environment. The default ARCH is the host where make is executed.
/*当为其他体系结构执行交叉编译的时候ARCH应该被设置为目标体系结构,而且需要选择不同的gcc和bin(二进制)工具集。ARCH可以通过make的命令行方式设置,也可以通过环境变量来设置,默认ARCH为make执行的主机体系结构*/
# CROSS_COMPILE specify the prefix used for all executables used during compilation. Only gcc and related bin-utils #executables are prefixed with $(CROSS_COMPILE).
# CROSS_COMPILE can be set on the command line
# make CROSS_COMPILE=ia64-linux-
# Alternatively CROSS_COMPILE can be set in the environment.
# Default value for CROSS_COMPILE is not to prefix executables
# Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
/*交叉编译变量(CROSS_COMPILE)用于指定在编译期间所有可执行文件使用的前缀,仅仅gcc工具和可执行的二进制工具集使用CROSS_COMPILE 变量作为前缀。可以通过make的命令行方式给CROSS_COMPILE变量赋值,也可以通过环境变量赋值,默认CROSS_COMPILE变量没有可执行的前缀。需要注意的是,某些体系结构在arch/*/Makefile中给变量CROSS_COMPILE赋值*/
export KBUILD_BUILDHOST := $(SUBARCH)
ARCH ?= $(SUBARCH)
CROSS_COMPILE ?=
/*使用export将KBUILD_BUILDHOST变量传递给子make,使用条件赋值?=方式给变量ARCH和CROSS_COMPILE赋值,在嵌入式系统交叉编译工具的设置经常要修改此处设置交叉编译器的路径;比如为ARM CPU进行交叉编程,这里的设置可以修改为:ARCH = arm CROSS_COMPILE = arm-linux- */
# Architecture as present in compile.h
UTS_MACHINE := $(ARCH)
SRCARCH := $(ARCH)
# Additional ARCH settings for x86
ifeq ($(ARCH),i386)
SRCARCH := x86
endif
ifeq ($(ARCH),x86_64)
SRCARCH := x86
endif
# Additional ARCH settings for sparc
ifeq ($(ARCH),sparc64)
SRCARCH := sparc
endif
# Additional ARCH settings for sh
ifeq ($(ARCH),sh64)
SRCARCH := sh
endif
# Where to locate arch specific headers
hdr-arch := $(SRCARCH)
ifeq ($(ARCH),m68knommu)
hdr-arch := m68k
endif
KCONFIG_CONFIG ?= .config
/*如果没有指定配置文件则使用默认配置文件,.config为默认的配置文件,赋值给KCONFIG_CONFIG变量供后面的规则使用*/
# SHELL used by kbuild
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi ; fi)
/*使用make的shell函数来执行shell程序,使用if语句的-x选项来判断程序是否可以执行,BASH为shell的内部变量,在make中使用双$$来引用变量*/
HOSTCC = gcc
HOSTCXX = g++
HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
HOSTCXXFLAGS = -O2
/*定义C、C++的编译器和编译选项*/
# Decide whether to build built-in, modular, or both.
# Normally, just do built-in.
/*决定是否编译为内置对象、模块、或者两者同时编译,通常编译为内置对象*/
KBUILD_MODULES :=
KBUILD_BUILTIN := 1
# If we have only "make modules", don't compile built-in objects.
# When we're building modules with modversions, we need to consider
# the built-in objects during the descend as well, in order to
# make sure the checksums are up to date before we record them.
ifeq ($(MAKECMDGOALS),modules)
KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1)
endif
/*如果make命令行使用了make modules命令,而且变量CONFIG_MODVERSIONS有定义,那么KBUILD_BUILTIN 变量赋值为1*/
# If we have "make <whatever> modules", compile modules
# in addition to whatever we do anyway.
# Just "make" or "make all" shall build modules as well
ifneq ($(filter all _all modules,$(MAKECMDGOALS)),)
KBUILD_MODULES := 1
endif
/*如果make的终极目标列表中有all、_all、modules中任何一个,变量KBUILD_MODULES赋值为1*/
ifeq ($(MAKECMDGOALS),)
KBUILD_MODULES := 1
endif
/*如果make终极目标列表变量MAKECMDGOALS为空,KBUILD_MODULES也赋值为1*/
export KBUILD_MODULES KBUILD_BUILTIN
export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD
/*导出变量到子make*/
# Beautify output
# Normally, we echo the whole command before executing it. By making
# that echo $($(quiet)$(cmd)), we now have the possibility to set
# $(quiet) to choose other forms of output instead, e.g.
# quiet_cmd_cc_o_c = Compiling $(RELDIR)/$@
# cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
# If $(quiet) is empty, the whole command will be printed.
# If it is set to "quiet_", only the short version will be printed.
# If it is set to "silent_", nothing will be printed at all, since
# the variable $(silent_cmd_cc_o_c) doesn't exist.
# A simple variant is to prefix commands with $(Q) - that's useful
# for commands that shall be hidden in non-verbose mode.
# $(Q)ln $@ :<
# If KBUILD_VERBOSE equals 0 then the above command will be hidden.
# If KBUILD_VERBOSE equals 1 then the above command is displayed.
ifeq ($(KBUILD_VERBOSE),1)
quiet =
Q =
else
quiet=quiet_
Q = @
endif
# If the user is running make -s (silent mode), suppress echoing of commands
ifneq ($(findstring s,$(MAKEFLAGS)),)
quiet=silent_
endif
export quiet Q KBUILD_VERBOSE
/*上面这几个变量用于控制是否需要回显*/
# Look for make include files relative to root of kernel src
MAKEFLAGS += --include-dir=$(srctree)
/*将内核源码树目录追加到MAKEFLAGS 变量中供所有子make使用*/
# We need some generic definitions (do not try to remake the file).
$(srctree)/scripts/Kbuild.include: ; /*如前面介绍使用空命令来禁止make的隐含规则*/
include $(srctree)/scripts/Kbuild.include
/*“ include”指示符告诉 make 暂停读取当前的 Makefile,而转去读取“ include”指定的一个或者多个文件,完成以后再继续当前 Makefile 的读取。Kbuild.include文件中是一些通用的函数和变量的定义,不要去改变。*/
# 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
AWK = awk
GENKSYMS = scripts/genksyms/genksyms
DEPMOD = /sbin/depmod
KALLSYMS = scripts/kallsyms
PERL = perl
CHECK = sparse
CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
-Wbitwise -Wno-return-void $(CF)
MODFLAGS = -DMODULE
CFLAGS_MODULE = $(MODFLAGS)
AFLAGS_MODULE = $(MODFLAGS)
LDFLAGS_MODULE =
CFLAGS_KERNEL =
AFLAGS_KERNEL =
/*这里是设置编译连接的默认程序,都是变量赋值操作*/
# Use LINUXINCLUDE when you must reference the include/ directory.
# Needed to be compatible with the O= option
LINUXINCLUDE := -Iinclude \
$(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include) \
-I$(srctree)/arch/$(hdr-arch)/include \
-include include/linux/autoconf.h
KBUILD_CPPFLAGS := -D__KERNEL__
KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
-fno-strict-aliasing -fno-common \
-Werror-implicit-function-declaration \
-fno-delete-null-pointer-checks
KBUILD_AFLAGS := -D__ASSEMBLY__
/*定义内核编译时包含头文件的路径,c、c++编译及汇编时候的标志*/
# Read KERNELRELEASE from include/config/kernel.release (if it exists)
KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
/*使用make的shell函数执行cat命令给变量赋值,如果文件不存在,则将错误消息丢弃*/
export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION
export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC
export CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL UTS_MACHINE
export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE
export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
/*使用export将变量传递到子make*/
# When compiling out-of-tree modules, put MODVERDIR in the module
# tree rather than in the kernel tree. The kernel tree might
# even be read-only.
export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions
/*由于内核树或许是只读的,因此在编译内核树外的模块的时候,需要为这些编译的模块指定存储的目录,变量MODVERDIR 就是模块存储目录,如果KBUILD_EXTMOD为空则默认目录为.tmp_versions*/
# Files to ignore in find ... statements
RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS -o -name .pc -o -name .hg -o -name .git \) -prune -o
export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn --exclude CVS --exclude .pc --exclude .hg --exclude .git
# ===========================================================================
# Rules shared between *config targets and build targets
# Basic helpers built in scripts/
PHONY += scripts_basic /*追加一个make目标,进入scripts/basic目录可以看到有Docproc.c和Fixdep.c两个文件,这里生成基本的scripts配置工具fixdep docproc*/
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
/*build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj 定义在Kbuild.include中,我们在前面包含Kbuild.include文件,Makefile.build中第一句是src := $(obj),因此Makefile.build中需要带入参数obj用于赋给src,这里会执行/scripts/Makefile.build 文件*/
# To avoid any implicit rule to kick in, define an empty command.
scripts/basic/%: scripts_basic ; /*使用空命令来禁止make的隐含规则*/
PHONY += outputmakefile
# outputmakefile generates a Makefile in the output directory, if using a
# separate output directory. This allows convenient use of make in the
# output directory.
outputmakefile:
ifneq ($(KBUILD_SRC),)
$(Q)ln -fsn $(srctree) source
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif
/*如果使用了一个独立的输出目录,outputmakefile目标在输出目录中产生一个Makefile文件,这可以允许在输出目录中很方便的使用make。这个规则的命令运行一个shell脚本scripts/mkmakefile,并传递四个参数。这个脚本主要是在$(objtree)参数指定的目录中生成一个Makefile文件。*/
# To make sure we do not include .config for any of the *config targets catch them early, and hand them over to #scripts/kconfig/Makefile
# It is allowed to specify more targets when calling make, including mixing *config targets and build targets.
# For example 'make oldconfig all'.
# Detect when mixed targets is specified, and make a second invocation
# of make so .config is not included in this case either (for *config).
/*上面的英文大概意思是说make后面的目标可以为配置目标(*config targets)、编译目标(build targets)、或者二者的混合目标,在使用的时候通过设置不同的变量来别处理。
make后面的目标包括三类:
(1)和.config无关的目标即(no_dot_config-targets)比如:clean,mrproper,distclean,
headers_install,kernelrelease,kernelversion等等;
(2)和.config相关的目标dot_config
a:配置目标(config targets)主要是产生.config文件
b:构建目标(build targets)主要是使用.config像all,vmlinux,modules,zImage,等等
(3)single targets
上面没有列出的,如:dir/,dir/file,dir/file.ko*/
no-dot-config-targets := clean mrproper distclean \
cscope TAGS tags help %docs check% \
include/linux/version.h headers_% \
kernelrelease kernelversion
config-targets := 0
mixed-targets := 0
dot-config := 1
/*对变量做初始化赋值*/
ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
dot-config := 0
endif
endif
/*如果终极目标列表变量MAKECMDGOALS只包含no-dot-config-targets变量中的目标,则make不依赖.config隐含文件*/
ifeq ($(KBUILD_EXTMOD),)
ifneq ($(filter config %config,$(MAKECMDGOALS)),)
config-targets := 1
ifneq ($(filter-out config %config,$(MAKECMDGOALS)),)
mixed-targets := 1
endif
endif
endif
/*如果make命令行带有menuconfig/xconfig等目标,则表示这是一条配置命令,如make menuconfig,那么变量config-targets置为1,标识应该生成.config文件;如果不仅有config目标,还有其他目标,表示为混合目标模式*/
ifeq ($(mixed-targets),1)
# ===========================================================================
# We're called with mixed targets (*config and build targets).
# Handle them one by one.
/*对于一个没有依赖而只有命令行的双冒号规则,当引用此目标时,规则的命令将会被无条件执行。*/
%:: FORCE
$(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@
/*%代表make 后带任何目标都执行该命令,从代码中可以看出,这里使用了一个双冒号的模式匹配规则。百分号代表任何目标都使用这个规则,其中$(srctree) 为内核代码树所在目 录 , KBUILD_SRC 定义为目标。所以如果 make 命令为 : make s3c2410_defconfig all 那么构建系统就会分别执行下面两条命令:make -C $(srctree) KBUILD_SRC= s3c2410_defconfig;make -C $(srctree) KBUILD_SRC= all;这其实和简单的用手动的输入两条连续命令(make s3c2410_defconfig 和 make all) 是一样效果的。 */
else
ifeq ($(config-targets),1) /*如果是配置目标,则执行配置操作*/
# ===========================================================================
# *config targets only - make sure prerequisites are updated, and descend in scripts/kconfig to make the *config target
# Read arch specific Makefile to set KBUILD_DEFCONFIG as needed.
# KBUILD_DEFCONFIG may point out an alternative default configuration used for 'make defconfig'
/*如果是配置目标要确定先决条件是最新的,并且进入到scripts/kconfig目录执行配置目标,读取体系结构对应的Makefile文件,同时设置KBUILD_DEFCONFIG变量*/
include $(srctree)/arch/$(SRCARCH)/Makefile /*直接包含平台架构下的makefile*/
export KBUILD_DEFCONFIG KBUILD_KCONFIG
config: scripts_basic outputmakefile FORCE
$(Q)mkdir -p include/linux include/config
$(Q)$(MAKE) $(build)=scripts/kconfig $@
%config: scripts_basic outputmakefile FORCE
$(Q)mkdir -p include/linux include/config
$(Q)$(MAKE) $(build)=scripts/kconfig $@
/*这两个是配置目标的生成规则,如前面所叙述,build变量在Kbuild.include中定义,这个文件已经被包含到当前Makefile中。build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj,Makefile.build中第一句是src := $(obj)。比如在顶层目录执行make menuconfig,在这里展开就是make -f scripts/Makefile.build obj=scripts/kconfig menuconfig;而scripts/Makefile.build 会包含scripts/kconfig/Makefile,在scripts/kconfig/Makefile中有menuconfig目标如下:
menuconfig: $(obj)/mconf
$< $(Kconfig)
$<表示依赖文件,$(obj)/mconf是一个解析Kconfig的程序,$(Kconfig)是被解析的文件*/
else
# ===========================================================================
# Build targets only - this includes vmlinux, arch specific targets, clean
# targets and others. In general all targets except *config targets.
/*除了配置目标外的所有目标的编译*/
ifeq ($(KBUILD_EXTMOD),)
# Additional helpers built in scripts/
# Carefully list dependencies so we do not try to build scripts twice
# in parallel
PHONY += scripts
scripts: scripts_basic include/config/auto.conf
$(Q)$(MAKE) $(build)=$(@)
/*这里生成scripts_basic include/config/auto.conf*/
# Objects we will link into vmlinux / subdirs we need to visit
init-y := init/
drivers-y := drivers/ sound/ firmware/
net-y := net/
libs-y := lib/
core-y := usr/
endif # KBUILD_EXTMOD
ifeq ($(dot-config),1) /*若.config已经被配置,则可包含产生的auto.conf文件*/
# Read in config
-include include/config/auto.conf
/*通常我们在 Makefile 中可使用“ -include”来代替“ include”,来忽略由于包含文件不存在或者无法创建时的错误提示(“ -”的意思是告诉 make,忽略此操作的错误。make 继续执行)*/
ifeq ($(KBUILD_EXTMOD),)
# Read in dependencies to all Kconfig* files, make sure to run oldconfig if changes are detected.
-include include/config/auto.conf.cmd
/*读所有Kconfig*文件的依赖关系,如果有任何改变,必须运行make oldconfig*/
# To avoid any implicit rule to kick in, define an empty command
$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;
/*使用空命令来避免隐含规则的干扰*/
# If .config is newer than include/config/auto.conf, someone tinkered with it and forgot to run make oldconfig.
# if auto.conf.cmd is missing then we are probably in a cleaned tree so we execute the config step to be sure to catch #updated Kconfig files
/*如果.config文件比include/config/auto.conf文件更新,那么可能是有人修改了.config文件而忘记运行make oldconfig命令。如果auto.conf.cmd文件丢失,我们可能处于执行了clean命令的源码树中,因此我们必须执行配置步骤来确保获得更新了的Kconfig文件*/
include/config/auto.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig /*规则命令产生新的auto.conf文件*/
else
# external modules needs include/linux/autoconf.h and include/config/auto.conf
# but do not care if they are up-to-date. Use auto.conf to trigger the test
PHONY += include/config/auto.conf
/*外部模块需要include/linux/autoconf.h 和include/config/auto.conf文件,但不需要关心他们是否是最新的,使用auto.conf来触发测试*/
include/config/auto.conf:
$(Q)test -e include/linux/autoconf.h -a -e $@ || ( \
echo; \
echo " ERROR: Kernel configuration is invalid."; \
echo " include/linux/autoconf.h or $@ are missing."; \
echo " Run 'make oldconfig && make prepare' on kernel src to fix it."; \
echo; \
/bin/false)
/*使用shell函数test来测试文件autoconf.h和auto.conf是否存在,如果不存在给出错误提示*/
endif # KBUILD_EXTMOD
else
# Dummy target needed, because used as prerequisite
include/config/auto.conf: ; /*使用一个假目标来作为先决条件,这里使用了空命令来禁止隐含规则*/
endif # $(dot-config)
# The all: target is the default when no target is given on the command line.
# This allow a user to issue only 'make' to build a kernel including modules
# Defaults vmlinux but it is usually overridden in the arch makefile
/*当在命令行没有给出目标的时候,默认的目标是all,这就使得用户可以在命令行只使用make就可以编译内核和模块,默认的vmlinux通常会在体系机构的makefile中被重载*/
all: vmlinux
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS += -Os
else
KBUILD_CFLAGS += -O2
endif
include $(srctree)/arch/$(SRCARCH)/Makefile /*包含跟体系结构相关的Makefile文件*/
ifneq ($(CONFIG_FRAME_WARN),0)
KBUILD_CFLAGS += $(call cc-option,-Wframe-larger-than=${CONFIG_FRAME_WARN})
endif
/*使用make的call函数调用用户自定义函数给KBUILD_CFLAGS变量追加编译选项,cc-option定义在Kbuild.include中,Kbuild.include文件中定义的是一些通用的函数和变量*/
# Force gcc to behave correct even for buggy distributions
ifndef CONFIG_CC_STACKPROTECTOR
KBUILD_CFLAGS += $(call cc-option, -fno-stack-protector)
endif
ifdef CONFIG_FRAME_POINTER
KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
else
KBUILD_CFLAGS += -fomit-frame-pointer
endif
ifdef CONFIG_DEBUG_INFO
KBUILD_CFLAGS += -g
KBUILD_AFLAGS += -gdwarf-2
endif
ifdef CONFIG_FUNCTION_TRACER
KBUILD_CFLAGS += -pg
endif
# We trigger additional mismatches with less inlining
ifdef CONFIG_DEBUG_SECTION_MISMATCH
KBUILD_CFLAGS += $(call cc-option, -fno-inline-functions-called-once)
endif
# arch Makefile may override CC so keep this after arch Makefile is included
NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
CHECKFLAGS += $(NOSTDINC_FLAGS)
# warn about C99 declaration after statement
KBUILD_CFLAGS += $(call cc-option,-Wdeclaration-after-statement,)
# disable pointer signed / unsigned warnings in gcc 4.0
KBUILD_CFLAGS += $(call cc-option,-Wno-pointer-sign,)
# disable invalid "can't wrap" optimizations for signed / pointers
KBUILD_CFLAGS += $(call cc-option,-fno-strict-overflow)
# revert to pre-gcc-4.4 behaviour of .eh_frame
KBUILD_CFLAGS += $(call cc-option,-fno-dwarf2-cfi-asm)
# Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments But warn user when we do so
/*增加用户提供的CPPFLAGS, AFLAGS and CFLAGS作为最后的赋值,这样做的时候需要提醒用户*/
warn-assign = \
$(warning "WARNING: Appending $$K$(1) ($(K$(1))) from $(origin K$(1)) to kernel $$$(1)")
/*函数“ warning”类似于函数“ error”,区别在于它不会导致致命错误( make 不退出),而只是提示“ TEXT…”, make 的执行过程继续。*/
ifneq ($(KCPPFLAGS),)
$(call warn-assign,CPPFLAGS)
KBUILD_CPPFLAGS += $(KCPPFLAGS)
endif
ifneq ($(KAFLAGS),)
$(call warn-assign,AFLAGS)
KBUILD_AFLAGS += $(KAFLAGS)
endif
ifneq ($(KCFLAGS),)
$(call warn-assign,CFLAGS)
KBUILD_CFLAGS += $(KCFLAGS)
endif
# Use --build-id when available.
LDFLAGS_BUILD_ID = $(patsubst -Wl$(comma)%,%,\
$(call ld-option, -Wl$(comma)--build-id,))
LDFLAGS_MODULE += $(LDFLAGS_BUILD_ID)
LDFLAGS_vmlinux += $(LDFLAGS_BUILD_ID)
ifeq ($(CONFIG_STRIP_ASM_SYMS),y)
LDFLAGS_vmlinux += -X
endif
/*变量CONFIG_STRIP_ASM_SYMS包含在各个CPU体系结构目录下Configs目录下的配置文件中,而前面包含了对应体系结构的Makefile文件,体系结构的Makefile文件会包含Configs目录下对应的配置文件来设置这个变量*/
# Default kernel image to build when no specific target is given.
# KBUILD_IMAGE may be overruled on the command line or set in the environment
# Also any assignments in arch/$(ARCH)/Makefile take precedence over this default value
export KBUILD_IMAGE ?= vmlinux
/*这里通过条件赋值的方式给KBUILD_IMAGE变量赋值,如果在make命令行或者环境变量或者对应体系结构的Makefile中已经给KBUILD_IMAGE赋值,那么这里就不会再进行赋值 */
# INSTALL_PATH specifies where to place the updated kernel and system map images. Default is /boot, but you can set it #to other values
export INSTALL_PATH ?= /boot
/*最新的内核和系统映像默认放置在/boot目录*/
# 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 argument can be passed to make if needed.
MODLIB = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)
export MODLIB
/*可以通过INSTALL_MOD_PATH变量给模块指定安装目录*/
# INSTALL_MOD_STRIP, if defined, will cause modules to be stripped after they are installed. If INSTALL_MOD_STRIP is #'1', then the default option --strip-debug will be used. Otherwise, INSTALL_MOD_STRIP will used as the options to the #strip command.
ifdef INSTALL_MOD_STRIP
ifeq ($(INSTALL_MOD_STRIP),1)
mod_strip_cmd = $(STRIP) --strip-debug
else
mod_strip_cmd = $(STRIP) $(INSTALL_MOD_STRIP)
endif # INSTALL_MOD_STRIP=1
else
mod_strip_cmd = true
endif # INSTALL_MOD_STRIP
export mod_strip_cmd
/*设置mod_strip_cmd变量,mod_strip_cmd变量在Scripts/Makefile.modinst文件中使用,在后面对模块的编译部分会保护Scripts/Makefile.modinst文件,所以用exoprt导出到子make中*/
ifeq ($(KBUILD_EXTMOD),)/*如果外部模块为空,即编译内核*/
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
/*core-y目标需要添加各个模块*/
vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
$(core-y) $(core-m) $(drivers-y) $(drivers-m) \
$(net-y) $(net-m) $(libs-y) $(libs-m)))
/*vmlinux-dirs即我们编译内核需要添加的各个模块所在的内核目录*/
vmlinux-alldirs := $(sort $(vmlinux-dirs) $(patsubst %/,%,$(filter %/, \
$(init-n) $(init-) \
$(core-n) $(core-) $(drivers-n) $(drivers-) \
$(net-n) $(net-) $(libs-n) $(libs-))))
init-y := $(patsubst %/, %/built-in.o, $(init-y))
core-y := $(patsubst %/, %/built-in.o, $(core-y))
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
net-y := $(patsubst %/, %/built-in.o, $(net-y))
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y := $(libs-y1) $(libs-y2)
/*使用make的模式替换函数在各个目录下加入built-in.o和lib.a文件,vmlinux-main即由各个目录下的build-in.o和lib.a链接而成*/
# Build vmlinux
# vmlinux is built from the objects selected by $(vmlinux-init) and $(vmlinux-main). Most are built-in.o files from top-level #directories in the kernel tree, others are specified in arch/$(ARCH)/Makefile.
# Ordering when linking is important, and $(vmlinux-init) must be first.
# vmlinux
# ^
# |
# +-< $(vmlinux-init)
# | +--< init/version.o + more
# |
# +--< $(vmlinux-main)
# | +--< driver/built-in.o mm/built-in.o + more
# |
# +-< kallsyms.o (see description in CONFIG_KALLSYMS section)
# vmlinux version (uname -v) cannot be updated during normal descending-into-subdirs phase since we do not yet know if #we need to update vmlinux.
# Therefore this step is delayed until just before final link of vmlinux - except in the kallsyms case where it is done just before #adding the symbols to the kernel.
# System.map is generated to document addresses of all kernel symbols
/*vmlinux是由$(vmlinux-init)和$(vmlinux-main)变量中选择的目标对象编译而成,这些目标对象多数是内核树中顶层目录的built-in.o文件,其他目标对象是由arch/$(ARCH)/Makefile文件中指定。在链接成vmlinux文件的时候,顺序是很重要的,$(vmlinux-init)必须位于文件头。在通常递归到子目录阶段,由于我们不知道是否需要跟新vmlinux,因此vmlinux的版本号(uname -v)不能被跟新,版本号的更新延时到vmlinux最后链接之前。System.map文件中保存所有内核符号的地址*/
vmlinux-init := $(head-y) $(init-y) /*head-y定义在对应体系结构目录的Makefile文件中*/
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux-all := $(vmlinux-init) $(vmlinux-main)
vmlinux-lds := arch/$(SRCARCH)/kernel/vmlinux.lds /*使用与架构相关的链接文件lds*/
export KBUILD_VMLINUX_OBJS := $(vmlinux-all)
# Rule to link vmlinux - also used during CONFIG_KALLSYMS
# May be overridden by arch/$(ARCH)/Makefile
quiet_cmd_vmlinux__ ?= LD $@
cmd_vmlinux__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) -o $@ \
-T $(vmlinux-lds) $(vmlinux-init) \
--start-group $(vmlinux-main) --end-group \
$(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o FORCE ,$^)
/*vmlinux的链接规则,在CONFIG_KALLSYMS期间也被使用,可能在arch/$(ARCH)/Makefile文件中被重载*/
# Generate new vmlinux version
quiet_cmd_vmlinux_version = GEN .version
cmd_vmlinux_version = set -e; \
if [ ! -r .version ]; then \
rm -f .version; \
echo 1 >.version; \
else \
mv .version .old_version; \
expr 0$$(cat .old_version) + 1 >.version; \
fi; \
$(MAKE) $(build)=init
/*产生新的vmlinux版本*/
# Generate System.map
quiet_cmd_sysmap = SYSMAP
cmd_sysmap = $(CONFIG_SHELL) $(srctree)/scripts/mksysmap
/*产生符号表*/
# Link of vmlinux
# If CONFIG_KALLSYMS is set .version is already updated
# Generate System.map and verify that the content is consistent
# Use + in front of the vmlinux_version rule to silent warning with make -j2
# First command is ':' to allow us to use + in front of the rule
define rule_vmlinux__ /*命令包名字为rule_vmlinux__*/
:
$(if $(CONFIG_KALLSYMS),,+$(call cmd,vmlinux_version))
/*cmd定义在Kbuild.include文件中,这里展开为cmd_vmlinux_version*/
$(call cmd,vmlinux__) /*展开为cmd_vmlinux__*/
$(Q)echo 'cmd_$@ := $(cmd_vmlinux__)' > $(@D)/.$(@F).cmd
/*取目标$@的目录部分 $(@D)和文件部分$(@F)形成.cmd文件即.vmlinux.cmd*/
$(Q)$(if $($(quiet)cmd_sysmap), \
echo ' $($(quiet)cmd_sysmap) System.map' &&) \
$(cmd_sysmap) $@ System.map; \
if [ $$? -ne 0 ]; then \
rm -f $@; \
/bin/false; \
fi;/*产生System.map文件并判断文件是否正确,如果不正确就删除*/
$(verify_kallsyms)
endef
/*定义vmlinux链接的命令包规则rule_vmlinux__*/
ifdef CONFIG_KALLSYMS
# Generate section listing all symbols and add it into vmlinux $(kallsyms.o) It's a three stage process:
# o .tmp_vmlinux1 has all symbols and sections, but __kallsyms is empty
# Running kallsyms on that gives us .tmp_kallsyms1.o with
# the right size - vmlinux version (uname -v) is updated during this step
# o .tmp_vmlinux2 now has a __kallsyms section of the right size,
# but due to the added section, some addresses have shifted.
# From here, we generate a correct .tmp_kallsyms2.o
# o The correct .tmp_kallsyms2.o is linked into the final vmlinux.
# o Verify that the System.map from vmlinux matches the map from
# .tmp_vmlinux2, just in case we did not generate kallsyms correctly.
# o If CONFIG_KALLSYMS_EXTRA_PASS is set, do an extra pass using
# .tmp_vmlinux3 and .tmp_kallsyms3.o. This is only meant as a
# temporary bypass to allow the kernel to be built while the
# maintainers work out what went wrong with kallsyms.
/*产生列出了所有符号列表的段,并且把符号列表作为kallsyms.o添加到vmlinux中,这个文件的生成分为3个阶段:一、.tmp_vmlinux1有所有符号和段,但是__kallsysms是空的,在.tmp_kallsyms.o上运行kallsyms就得到正确大小的.tmp_kallsyms2.o,vmlinux版本在这一步被更新。二、.tmp_vmlinux2现在有正确大小的__kallsyms段,但是由于增加了段,一些地址发生了偏移,这时我们就得到了正确的.tmp_kallsyms2.o,.tmp_kallsyms2.o被链接到最终的vmlinux中。对来至于vmlinux的System.map和来自于.tmp_vmlinux2的map做匹配校验,防止得到不正确的kallsyms。三、如果设置了CONFIG_KALLSYMS_EXTRA_PASS,使用.tmp_vmlinux3 和.tmp_kallsyms3.o再做一次*/
ifdef CONFIG_KALLSYMS_EXTRA_PASS
last_kallsyms := 3
else
last_kallsyms := 2
endif
kallsyms.o := .tmp_kallsyms$(last_kallsyms).o
define verify_kallsyms
$(Q)$(if $($(quiet)cmd_sysmap), \
echo ' $($(quiet)cmd_sysmap) .tmp_System.map' &&) \
$(cmd_sysmap) .tmp_vmlinux$(last_kallsyms) .tmp_System.map
$(Q)cmp -s System.map .tmp_System.map || \
(echo Inconsistent kallsyms data; \
echo Try setting CONFIG_KALLSYMS_EXTRA_PASS; \
rm .tmp_kallsyms* ; /bin/false )
endef
/*这个命令包首先用命令cmd_sysmap从.tmp_vmlinux产生.tmp_System.map,使用shell的cmp比较命令对System.map和.tmp_System.map做比较,-s表示如果有不同不打印出来,只返回比较结果*/
# Update vmlinux version before link
# Use + in front of this rule to silent warning about make -j1
# First command is ':' to allow us to use + in front of this rule
/*在连接前更新vmlinux的版本,在规则前使用+号可以静止make过程中的警告错误输出,第一个命令是冒号(:)允许我们在这个规则前面使用+号*/
cmd_ksym_ld = $(cmd_vmlinux__)
define rule_ksym_ld
:
+$(call cmd,vmlinux_version) /*cmd定义在Kbuild.include文件中,这里展开为cmd_vmlinux_version*/
$(call cmd,vmlinux__) /*展开为cmd_vmlinux_*/
$(Q)echo 'cmd_$@ := $(cmd_vmlinux__)' > $(@D)/.$(@F).cmd /*将echo产生的信息输入到以目标的目录和文件生成的.cmd文件中*/
endef
# Generate .S file with all kernel symbols /*产生的.S文件具有所有的内核符号*/
quiet_cmd_kallsyms = KSYM $@
cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) \
$(if $(CONFIG_KALLSYMS_ALL),--all-symbols) > $@
.tmp_kallsyms1.o .tmp_kallsyms2.o .tmp_kallsyms3.o: %.o: %.S scripts FORCE
$(call if_changed_dep,as_o_S)
/*if_changed_dep包含在Kbuild.include文件中,如果依赖条件比目标文件新或者命令行有变化,就需要执行命令*/
.tmp_kallsyms%.S: .tmp_vmlinux% $(KALLSYMS)
$(call cmd,kallsyms) /*展开为cmd_kallsyms*/
# .tmp_vmlinux1 must be complete except kallsyms, so update vmlinux version
.tmp_vmlinux1: $(vmlinux-lds) $(vmlinux-all) FORCE
$(call if_changed_rule,ksym_ld) /*执行cmd_ksym_ld*/
.tmp_vmlinux2: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms1.o FORCE
$(call if_changed,vmlinux__) /*执行cmd_vmlinux__*/
.tmp_vmlinux3: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms2.o FORCE
$(call if_changed,vmlinux__) /*执行cmd_vmlinux__*/
# Needs to visit scripts/ before $(KALLSYMS) can be used.
$(KALLSYMS): scripts ;
# Generate some data for debugging strange kallsyms problems
debug_kallsyms: .tmp_map$(last_kallsyms)
.tmp_map%: .tmp_vmlinux% FORCE
($(OBJDUMP) -h $< | $(AWK) '/^ +[0-9]/{print $$4 " 0 " $$2}'; $(NM) $<) | sort > $@
/*产生调试kallsyms问题的debug数据*/
.tmp_map3: .tmp_map2
.tmp_map2: .tmp_map1
endif # ifdef CONFIG_KALLSYMS
# Do modpost on a prelinked vmlinux. The finally linked vmlinux has
# relevant sections renamed as per the linker script.
quiet_cmd_vmlinux-modpost = LD $@
cmd_vmlinux-modpost = $(LD) $(LDFLAGS) -r -o $@ \
$(vmlinux-init) --start-group $(vmlinux-main) --end-group \
$(filter-out $(vmlinux-init) $(vmlinux-main) FORCE ,$^)
define rule_vmlinux-modpost
:
+$(call cmd,vmlinux-modpost)
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost $@
$(Q)echo 'cmd_$@ := $(cmd_vmlinux-modpost)' > $(dot-target).cmd
endef
# vmlinux image - including updated kernel symbols
/*vmlinux的依赖目标集,开始真正的做主目录下的映像文件*/
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
ifdef CONFIG_HEADERS_CHECK
$(Q)$(MAKE) -f $(srctree)/Makefile headers_check
endif
ifdef CONFIG_SAMPLES /*配置样本即调用Makefile.build 到Samples目录下make生成样本*/
$(Q)$(MAKE) $(build)=samples
endif
ifdef CONFIG_BUILD_DOCSRC
$(Q)$(MAKE) $(build)=Documentation
endif
$(call vmlinux-modpost) /*vmlinux-modpost没有被定义,是一条多余的没有被定义的变量*/
$(call if_changed_rule,vmlinux__)
/*if_changed_rule包含在Kbuild.include中,这里展开就是执行rule_vmlinux__,生成vmlinux镜像和System.map*/
$(Q)rm -f .old_version
# build vmlinux.o first to catch section mismatch errors early
ifdef CONFIG_KALLSYMS
.tmp_vmlinux1: vmlinux.o
endif
modpost-init := $(filter-out init/built-in.o, $(vmlinux-init))
vmlinux.o: $(modpost-init) $(vmlinux-main) FORCE
$(call if_changed_rule,vmlinux-modpost) /*执行rule_vmlinux-modpost命令包生成vmlinux.o*/
# The actual objects are generated when descending, make sure no implicit rule kicks in
$(sort $(vmlinux-init) $(vmlinux-main)) $(vmlinux-lds): $(vmlinux-dirs) ;
/*在递归的时候产生真正的目标,这里用空命令来确保没有隐含规则影响*/
# Handle descending into subdirectories listed in $(vmlinux-dirs)
# Preset locale variables to speed up the build process. Limit locale
# tweaks to this spot to avoid wrong language settings when running
# make menuconfig etc.
# Error messages still appears in the original language
/*在递归的进入到vmlinux-dirs变量中列出的子目录中进行处理时,预先设置局部变量用于展开编译过程。*/
PHONY += $(vmlinux-dirs)
$(vmlinux-dirs): prepare scripts
$(Q)$(MAKE) $(build)=$@
# Build the kernel release string
# The KERNELRELEASE value built here is stored in the file include/config/kernel.release, and is used when executing #several # make targets, such as "make install" or "make modules_install."
# The eventual kernel release string consists of the following fields,shown in a hierarchical format to show how smaller parts #are concatenated to form the larger and final value, with values coming from places like the Makefile, kernel config options, #make command line options and/or SCM tag information.
# $(KERNELVERSION)
# $(VERSION) eg, 2
# $(PATCHLEVEL) eg, 6
# $(SUBLEVEL) eg, 18
# $(EXTRAVERSION) eg, -rc6
# $(localver-full)
# $(localver)
# localversion* (files without backups, containing '~')
# $(CONFIG_LOCALVERSION) (from kernel config setting)
# $(localver-auto) (only if CONFIG_LOCALVERSION_AUTO is set)
# ./scripts/setlocalversion (SCM tag, if one exists)
# $(LOCALVERSION) (from make command line if provided)
# Note how the final $(localver-auto) string is included *only* if the kernel config option CONFIG_LOCALVERSION_AUTO #is selected. Also, at the moment, only git is supported but other SCMs can edit the script scripts/setlocalversion and add #the appropriate checks as needed.
/*上面使用分层格式描述了怎样构建内核的发布字符串*/
pattern = ".*/localversion[^~]*"
string = $(shell cat /dev/null \
`find $(objtree) $(srctree) -maxdepth 1 -regex $(pattern) | sort -u`)
localver = $(subst $(space),, $(string) \
$(patsubst "%",%,$(CONFIG_LOCALVERSION)))
# If CONFIG_LOCALVERSION_AUTO is set scripts/setlocalversion is called and if the SCM is know a tag from the SCM is #appended. The appended tag is determined by the SCM used.
# .scmversion is used when generating rpm packages so we do not loose the version information from the SCM when we #do the build of the kernel from the copied source
ifdef CONFIG_LOCALVERSION_AUTO
ifeq ($(wildcard .scmversion),)
_localver-auto = $(shell $(CONFIG_SHELL) \
$(srctree)/scripts/setlocalversion $(srctree))
else
_localver-auto = $(shell cat .scmversion 2> /dev/null)
endif
localver-auto = $(LOCALVERSION)$(_localver-auto)
endif
localver-full = $(localver)$(localver-auto)
# Store (new) KERNELRELASE string in include/config/kernel.release
kernelrelease = $(KERNELVERSION)$(localver-full)
include/config/kernel.release: include/config/auto.conf FORCE
$(Q)rm -f $@
$(Q)echo $(kernelrelease) > $@
/*上面步骤创建的linux内核发布信息存储在include/config/kernel.release文件中*/
# Things we need to do before we recursively start building the kernel or the modules are listed in "prepare".
# A multi level approach is used. prepareN is processed before prepareN-1.
# archprepare is used in arch Makefiles and when processed asm symlink,
# version.h and scripts_basic is processed / created.
/*在递归的开始编译内核或者模块前需要做的事情被罗列在prepare目标中,一种多级的方法被使用,prepareN是在prepareN-1之前被处理。archprepare被用在体系结构Makefiles中,当处理汇编的符号链接的时候,文件version.h和scripts_basic被处理或者创建。*/
# Listed in dependency order /*按依赖顺序列出*/
PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3
# prepare3 is used to check if we are building in a separate output directory, and if so do:
# 1) Check that make has not been executed in the kernel src $(srctree)
# 2) Create the include2 directory, used for the second asm symlink
/*prepare3被用于检测是否我们在一个独立的输出目录编译,如果是需要做两件事:1、检查但在内核源码中不执行的make;2、创建include2目录用于第二次汇编符号链接*/
prepare3: include/config/kernel.release
ifneq ($(KBUILD_SRC),)
@$(kecho) ' Using $(srctree) as source for kernel'
$(Q)if [ -f $(srctree)/.config -o -d $(srctree)/include/config ]; then \ /*如果存在文件.config或者目录config*/
echo " $(srctree) is not clean, please run 'make mrproper'";\ /*给出提示,先运行make mrproper*/
echo " in the '$(srctree)' directory.";\
/bin/false; \
fi;
$(Q)if [ ! -d include2 ]; then \ /*如果没有目录include2,创建目录,*/
mkdir -p include2; \
ln -fsn $(srctree)/include/asm-$(SRCARCH) include2/asm; \ /*做符号链接到include2/asm目录*/
fi
endif
# prepare2 creates a makefile if using a separate output directory
prepare2: prepare3 outputmakefile
prepare1: prepare2 include/linux/version.h include/linux/utsrelease.h \
include/asm include/config/auto.conf
$(cmd_crmodverdir) /*在后面定义的命令*/
archprepare: prepare1 scripts_basic
prepare0: archprepare FORCE
$(Q)$(MAKE) $(build)=.
$(Q)$(MAKE) $(build)=. missing-syscalls
# All the preparing..
prepare: prepare0
# Leave this as default for preprocessing vmlinux.lds.S, which is now done in arch/$(ARCH)/kernel/Makefile
export CPPFLAGS_vmlinux.lds += -P -C -U$(ARCH)
# The asm symlink changes when $(ARCH) changes. Detect this and ask user to run make mrproper
# If asm is a stale symlink (point to dir that does not exist) remove it
define check-symlink
set -e; \ /*et -e 是设置一种shell状态即如果它执行的任何一条命令的退出码非零都立刻终止*/
if [ -L include/asm ]; then \
asmlink=`readlink include/asm | cut -d '-' -f 2`; \
if [ "$$asmlink" != "$(SRCARCH)" ]; then \
echo "ERROR: the symlink $@ points to asm-$$asmlink but asm-$(SRCARCH) was expected"; \
echo " set ARCH or save .config and run 'make mrproper' to fix it"; \
exit 1; \
fi; \
test -e $$asmlink || rm include/asm; \
elif [ -d include/asm ]; then \
echo "ERROR: $@ is a directory but a symlink was expected";\
exit 1; \
fi
endef
# We create the target directory of the symlink if it does not exist so the test in check-symlink works and we have a
# directory for generated filesas used by some architectures.
define create-symlink
if [ ! -L include/asm ]; then \
$(kecho) ' SYMLINK $@ -> include/asm-$(SRCARCH)'; \
if [ ! -d include/asm-$(SRCARCH) ]; then \
mkdir -p include/asm-$(SRCARCH); \
fi; \
ln -fsn asm-$(SRCARCH) $@; \
fi
endef
include/asm: FORCE
$(Q)$(check-symlink)
$(Q)$(create-symlink)
# Generate some files
# ---------------------------------------------------------------------------
# KERNELRELEASE can change from a few different places, meaning version.h
# needs to be updated, so this check is forced on all builds
uts_len := 64
define filechk_utsrelease.h
if [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then \
echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2; \
exit 1; \
fi; \ /*如果KERNELRELEASE长度大于64,则给出错误并退出*/
(echo \#define UTS_RELEASE \"$(KERNELRELEASE)\";) /*否则生成UTS_RELEASE的定义*/
endef
define filechk_version.h
(echo \#define LINUX_VERSION_CODE $(shell \
expr $(VERSION) \* 65536 + $(PATCHLEVEL) \* 256 + $(SUBLEVEL)); \
echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))';)
endef
include/linux/version.h: $(srctree)/Makefile FORCE
$(call filechk,version.h) /*展开为filechk_version.h命令包*/
include/linux/utsrelease.h: include/config/kernel.release FORCE
$(call filechk,utsrelease.h) /*展开为filechk_utsrelease.h命令包*/
PHONY += headerdep
headerdep:
$(Q)find include/ -name '*.h' | xargs --max-args 1 scripts/headerdep.pl
/*使用find函数在include目录下查找.h文件,通过管道按1行传入,用headerdep.pl进行处理*/
# ---------------------------------------------------------------------------
PHONY += depend dep
depend dep:
@echo '*** Warning: make $@ is unnecessary now.'
# ---------------------------------------------------------------------------
# Firmware install /*固件的安装路径和固件目标的编译规则*/
INSTALL_FW_PATH=$(INSTALL_MOD_PATH)/lib/firmware
export INSTALL_FW_PATH
PHONY += firmware_install
firmware_install: FORCE
@mkdir -p $(objtree)/firmware
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_install
# ---------------------------------------------------------------------------
# Kernel headers Default location for installed headers /*内核头文件的默认安装位置*/
export INSTALL_HDR_PATH = $(objtree)/usr
hdr-inst := -rR -f $(srctree)/scripts/Makefile.headersinst obj
# Find out where the Kbuild file is located to support
# arch/$(ARCH)/include/asm
hdr-dir = $(strip \
$(if $(wildcard $(srctree)/arch/$(hdr-arch)/include/asm/Kbuild), \
arch/$(hdr-arch)/include/asm, include/asm-$(hdr-arch)))
/*wildcard函数返回Kbuild、asm、 asm-$(hdr-arch)这三个目录,if函数根据是否有Kbuild目录来给hdr-dir变量赋值*/
# If we do an all arch process set dst to asm-$(hdr-arch)
hdr-dst = $(if $(KBUILD_HEADERS), dst=include/asm-$(hdr-arch), dst=include/asm)
PHONY += __headers
__headers: include/linux/version.h scripts_basic FORCE
$(Q)$(MAKE) $(build)=scripts scripts/unifdef
PHONY += headers_install_all
headers_install_all:
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/headers.sh install
PHONY += headers_install
headers_install: __headers
$(if $(wildcard $(srctree)/$(hdr-dir)/Kbuild),, \
$(error Headers not exportable for the $(SRCARCH) architecture))
$(Q)$(MAKE) $(hdr-inst)=include
$(Q)$(MAKE) $(hdr-inst)=$(hdr-dir) $(hdr-dst)
PHONY += headers_check_all
headers_check_all: headers_install_all
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/headers.sh check
PHONY += headers_check
headers_check: headers_install
$(Q)$(MAKE) $(hdr-inst)=include HDRCHECK=1
$(Q)$(MAKE) $(hdr-inst)=$(hdr-dir) $(hdr-dst) HDRCHECK=1
# ---------------------------------------------------------------------------
# Modules
ifdef CONFIG_MODULES /*配置模块变量,通过make menuconfig产生,包含在.config文件中*/
# By default, build modules as well
all: modules
# Build modules
# A module can be listed more than once in obj-m resulting in
# duplicate lines in modules.order files. Those are removed
# using awk while concatenating to the final file.
PHONY += modules
modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux)
$(Q)$(AWK) '!x[$$0]++' $(vmlinux-dirs:%=$(objtree)/%/modules.order) > $(objtree)/modules.order
@$(kecho) ' Building modules, stage 2.';
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modbuild
/*使用变量的替换引用,将vmlinux-dirs中的目录使用$(objtree)/%/modules.order来替换,再进行AWK处理,处理结果输入到$(objtree)/modules.order文件*/
# Target to prepare building external modules
PHONY += modules_prepare
modules_prepare: prepare scripts
# Target to install modules
PHONY += modules_install
modules_install: _modinst_ _modinst_post
PHONY += _modinst_
_modinst_:
@if [ -z "`$(DEPMOD) -V 2>/dev/null | grep module-init-tools`" ]; then \
echo "Warning: you may need to install module-init-tools"; \
echo "See http://www.codemonkey.org.uk/docs/post-halloween-2.6.txt";\
sleep 1; \
fi /*模块的安装需要/sbin/depmod工具,这个if语句检查是否安装了/sbin/depmod工具*/
@rm -rf $(MODLIB)/kernel
@rm -f $(MODLIB)/source
@mkdir -p $(MODLIB)/kernel
@ln -s $(srctree) $(MODLIB)/source
@if [ ! $(objtree) -ef $(MODLIB)/build ]; then \ /*f1 -ef f2表示:f1 和 f2 都硬连接到同一个文件*/
rm -f $(MODLIB)/build ; \
ln -s $(objtree) $(MODLIB)/build ; \
fi /*这个if语句表示如果 $(objtree)和$(MODLIB)/build没有连接到同一个文件则让他们连接到同一个文件*/
@cp -f $(objtree)/modules.order $(MODLIB)/
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modinst
# 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.
PHONY += _modinst_post
_modinst_post: _modinst_
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modinst
$(call cmd,depmod)
else # CONFIG_MODULES
# Modules not configured
# ---------------------------------------------------------------------------
modules modules_install: FORCE
@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 # CONFIG_MODULES
# Cleaning is done on three levels. /*三个层面上的目标清理,make mrproper包含make clean的功能,make distclean包含make mrproper*/
# make clean Delete most generated files Leave enough to build external modules
# make mrproper Delete the current configuration, and all generated files
# make distclean Remove editor backup files, patch leftover files and the like Directories & files removed with 'make clean'
CLEAN_DIRS += $(MODVERDIR)
CLEAN_FILES += vmlinux System.map \
.tmp_kallsyms* .tmp_version .tmp_vmlinux* .tmp_System.map
# Directories & files removed with 'make mrproper'
MRPROPER_DIRS += include/config include2 usr/include include/generated
MRPROPER_FILES += .config .config.old include/asm .version .old_version \
include/linux/autoconf.h include/linux/version.h \
include/linux/utsrelease.h \
include/linux/bounds.h include/asm*/asm-offsets.h \
Module.symvers Module.markers tags TAGS cscope*
# clean - Delete most, but leave enough to build external modules
clean: rm-dirs := $(CLEAN_DIRS)
clean: rm-files := $(CLEAN_FILES)
clean-dirs := $(addprefix _clean_,$(srctree) $(vmlinux-alldirs) Documentation)
/*增加前缀函数addprefix 为$(srctree) $(vmlinux-alldirs) Documentation中每个文件前添加前缀_clean_*/
PHONY += $(clean-dirs) clean archclean
$(clean-dirs):
$(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@)
clean: archclean $(clean-dirs)
$(call cmd,rmdirs) /*展开为执行cmd_rmdirs命令*/
$(call cmd,rmfiles) /*展开为执行cmd_rmfiles命令*/
@find . $(RCS_FIND_IGNORE) \
\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
-o -name '*.symtypes' -o -name 'modules.order' \
-o -name 'Module.markers' -o -name '.tmp_*.o.*' \) \
-type f -print | xargs rm -f
/*使用find函数在当前目录下寻找由-name指定的文件并打印出来,通过管道传递给xargs处理后,使用rm删除*/
# mrproper - Delete all generated files, including .config
mrproper: rm-dirs := $(wildcard $(MRPROPER_DIRS))
mrproper: rm-files := $(wildcard $(MRPROPER_FILES))
mrproper-dirs := $(addprefix _mrproper_,Documentation/DocBook scripts)
PHONY += $(mrproper-dirs) mrproper archmrproper
$(mrproper-dirs):
$(Q)$(MAKE) $(clean)=$(patsubst _mrproper_%,%,$@)
mrproper: clean archmrproper $(mrproper-dirs) /*这里看出make mrproper包含make clean*/
$(call cmd,rmdirs)
$(call cmd,rmfiles)
# distclean
PHONY += distclean
distclean: mrproper
@find $(srctree) $(RCS_FIND_IGNORE) \
\( -name '*.orig' -o -name '*.rej' -o -name '*~' \
-o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \
-o -name '.*.rej' -o -size 0 \
-o -name '*%' -o -name '.*.cmd' -o -name 'core' \) \
-type f -print | xargs rm -f
# Packaging of the kernel to various formats rpm target kept for backward compatibility
/*将内核打包为各种格式的rpm目标保存向后兼容*/
package-dir := $(srctree)/scripts/package
%pkg: include/config/kernel.release FORCE
$(Q)$(MAKE) $(build)=$(package-dir) $@
rpm: include/config/kernel.release FORCE
$(Q)$(MAKE) $(build)=$(package-dir) $@
# Brief documentation of the typical targets used
boards := $(wildcard $(srctree)/arch/$(SRCARCH)/configs/*_defconfig)
boards := $(notdir $(boards))
board-dirs := $(dir $(wildcard $(srctree)/arch/$(SRCARCH)/configs/*/*_defconfig))
board-dirs := $(sort $(notdir $(board-dirs:/=)))
help:
@echo 'Cleaning targets:'
@echo ' clean - Remove most generated files but keep the config and'
@echo ' enough build support to build external modules'
@echo ' mrproper - Remove all generated files + config + various backup files'
@echo ' distclean - mrproper + remove editor backup and patch files'
@echo ''
@echo 'Configuration targets:'
@$(MAKE) -f $(srctree)/scripts/kconfig/Makefile help
@echo ''
@echo 'Other generic targets:'
@echo ' all - Build all targets marked with [*]'
@echo '* vmlinux - Build the bare kernel'
@echo '* modules - Build all modules'
@echo ' modules_install - Install all modules to INSTALL_MOD_PATH (default: /)'
@echo ' firmware_install- Install all firmware to INSTALL_FW_PATH'
@echo ' (default: $$(INSTALL_MOD_PATH)/lib/firmware)'
@echo ' dir/ - Build all files in dir and below'
@echo ' dir/file.[ois] - Build specified target only'
@echo ' dir/file.ko - Build module including final link'
@echo ' modules_prepare - Set up for building external modules'
@echo ' tags/TAGS - Generate tags file for editors'
@echo ' cscope - Generate cscope index'
@echo ' kernelrelease - Output the release version string'
@echo ' kernelversion - Output the version stored in Makefile'
@echo ' headers_install - Install sanitised kernel headers to INSTALL_HDR_PATH'; \
echo ' (default: $(INSTALL_HDR_PATH))'; \
echo ''
@echo 'Static analysers'
@echo ' checkstack - Generate a list of stack hogs'
@echo ' namespacecheck - Name space analysis on compiled kernel'
@echo ' versioncheck - Sanity check on version.h usage'
@echo ' includecheck - Check for duplicate included header files'
@echo ' export_report - List the usages of all exported symbols'
@echo ' headers_check - Sanity check on exported headers'
@echo ' headerdep - Detect inclusion cycles in headers'; \
echo ''
@echo 'Kernel packaging:'
@$(MAKE) $(build)=$(package-dir) help
@echo ''
@echo 'Documentation targets:'
@$(MAKE) -f $(srctree)/Documentation/DocBook/Makefile dochelp
@echo ''
@echo 'Architecture specific targets ($(SRCARCH)):'
@$(if $(archhelp),$(archhelp),\
echo ' No architecture specific help defined for $(SRCARCH)')
@echo ''
@$(if $(boards), \
$(foreach b, $(boards), \
printf " %-24s - Build for %s\\n" $(b) $(subst _defconfig,,$(b));) \
echo '')
@$(if $(board-dirs), \
$(foreach b, $(board-dirs), \
printf " %-16s - Show %s-specific targets\\n" help-$(b) $(b);) \
printf " %-16s - Show all of the above\\n" help-boards; \
echo '')
@echo ' make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build'
@echo ' make V=2 [targets] 2 => give reason for rebuild of target'
@echo ' make O=dir [targets] Locate all output files in "dir", including .config'
@echo ' make C=1 [targets] Check all c source with $$CHECK (sparse by default)'
@echo ' make C=2 [targets] Force check of all c source with $$CHECK'
@echo ''
@echo 'Execute "make" or "make all" to build all targets marked with [*] '
@echo 'For further info see the ./README file'
help-board-dirs := $(addprefix help-,$(board-dirs))
help-boards: $(help-board-dirs)
boards-per-dir = $(notdir $(wildcard $(srctree)/arch/$(SRCARCH)/configs/$*/*_defconfig))
$(help-board-dirs): help-%:
@echo 'Architecture specific targets ($(SRCARCH) $*):'
@$(if $(boards-per-dir), \
$(foreach b, $(boards-per-dir), \
printf " %-24s - Build for %s\\n" $*/$(b) $(subst _defconfig,,$(b));) \
echo '')
# Documentation targets
%docs: scripts_basic FORCE
$(Q)$(MAKE) $(build)=Documentation/DocBook $@
else # KBUILD_EXTMOD /*KBUILD_EXTMOD 不为空*/