VERSION = 4
PATCHLEVEL = 1
SUBLEVEL = 15
EXTRAVERSION =
NAME = Series 4800
一开始就是对linux内核版本的说明
MAKEFLAGS += -rR --include-dir=$(CURDIR)
MAKEFLAGS变量是用来传递MAKE选项的,如果在命令行中make后面有选项,则在此处,会在原来make选项后加上 -rR --include-dir=$(CURDIR),对于递归调用同样适用,如果下级子目录makefile中也有MAKEFLAGS变量的设定,则会继承上级makefile中MAKEFLAGS的值,除非显示设定为不继承。
例如,命令行中执行的make --no-print-directory
则此处的MAKEFLAGS值为 --no-print-directory -rR --include-dir=当前目录。
# Avoid funny character set dependencies
unexport LC_ALL
LC_COLLATE=C
LC_NUMERIC=C
export LC_COLLATE LC_NUMERIC
C语言的区域设置,估计都要设,去除本地化设置,具体作用也不深究了,看自带的注释,应该就是为了避免一些比较奇怪的字符集依赖。uxexport表示不向子makefile传递,export则需要向子makefile传递。
# Avoid interference with shell env settings
unexport GREP_OPTIONS
shell下有个命令就是grep,grep搜索的时候shell下应该有搜索属性或者选项,比如看到有人需要把搜索结果高亮,就会显式的加上export GREP_OPTIONS=’–color=XXX’ ; color有三个值供选择: never always auto ;这里unexport掉,就是为了避免和shell env下的设置冲突。
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif
这里用到了makefile里头的origin语法。origin函数不像其它函数,他并不直接操作变量的值,只是告诉你这个变量是从哪里来的,可以理解为追踪出生地。
其语法是:
$(origin variable)
注意这里是变量的名字,不是引用,所以不要使用“$”字符。origin函数会以返回值告诉你这个变量的“出生情况”(这个变量从哪里来的?)。
此处就是追踪变量V,是不是从命令行来的,如果是,返回"command line",否则返回"undefined"。紧接着后面就判断,如果来源于命令行,则将KBUILD的值等于变量V的值。
举个例子,命令行下输入下列命令,返回值分别为:
Shell命令 | 返回值 | KBUILD值 |
---|---|---|
make V=1 | command line | 1 |
make | undefined | 空 |
make V | undefined | 空 |
如果此变量是来源于命令行,则将变量KBUILD_VERBOSE赋值为变量V的值。
ifeq ($(KBUILD_VERBOSE),1)
quiet =
Q =
else
quiet=quiet_
Q = @
endif
Q :控制编译的时候是否在终端输出完整的命令。
quiet:控制输出命令长短
# If the user is running make -s (silent mode), suppress echoing of
# commands
ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
quiet=silent_
endif
else # make-3.8x
ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
quiet=silent_
endif
endif
ifneq ($(filter 4.%,$(MAKE_VERSION)),)
,实际就是如果变量MAKE_VERSION满足通配符4.$,就走此分支。export quiet Q KBUILD_VERBOSE
# 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),)
# OK, Make called in directory where kernel src resides
# Do we want to locate output files in a separate directory?
ifeq ("$(origin O)", "command line")
KBUILD_OUTPUT := $(O)
endif
KBUILD_SRC:在OBJ目录调用make时需要设定此变量的值。
KBUILD_OUTPUT:指定的输出文件存放位置
如果想把输出文件存放在指定目录,有两种办法
O=赋值优先于KBUILD_输出
PHONY := _all
这里开始都是默认目标了,就是没用make xxx指定目标的时候,默认就是执行这个目标
_all:
# Cancel implicit rules on top Makefile
$(CURDIR)/Makefile Makefile: ;
这条命令也不是很明白,从注释上看,是为了阻止makefile使用隐含规则来构建目标。这条语句肯定是条空语句。查到大家给的说法是空命令行可以防止make执行时试图为重建这个目标去查找隐含命令(包括了使用隐含规则中的命令和“.DEFAULT”指定的命令),不是太懂,保留意见。
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 mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT) \
&& /bin/pwd) ②
$(if $(KBUILD_OUTPUT),, \
$(error failed to create output directory "$(saved-output)")) ③
PHONY += $(MAKECMDGOALS) sub-make ④
$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make
@: ⑤
sub-make: FORCE
$(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR) \
-f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS)) ⑥
# Leave processing to above invocation of make
skip-makefile := 1 ⑦
endif # ifneq ($(KBUILD_OUTPUT),)
endif # ifeq ($(KBUILD_SRC),)
这段是一个整体,就一起看了。当KBUILD_OUTPUT不为空的时候,执行。
① 变量赋值
② shell下执行,先级联创建目录$KBUILD_OUTPUT,然后进入到这个目录,并且执行/bin/pwd,就是获取当前目录的完整路径,并赋值给变量KBUILD_OUTPUT
③ 判断KBUILD_OUTPUT是否为空,如果为空,就说明没有创建成功。
④ MAKECMDGOALS:记录了命令行参数指定的终极目标列表,没有通过参数指定终极目标时此变量为空。比如make all,那么MAKECMDGOALS的值就是all,make,MAKECMDGOALS的值就是空。此语句就是给终极目标列表在增加sub-make这个目标。
⑤ filter-out:
函数名称 :反过滤函数—filter-out。
语法: $(filter-out PATTERN…,TEXT)
函数功能 :和“filter”函数实现的功能相反。过滤掉字串“TEXT”中所有符合模式“PATTERN”的单词,保留所有不符合此模式的单词。可以有多个模式。存在多个模式时,模式表达式之间使用空格分割。
返回值 :空格分割的“TEXT”字串中所有不符合模式“PATTERN”的字串
在此处:
PATTERN: _all sub-make ( C U R D I R ) / M a k e f i l e T E X T : (CURDIR)/Makefile TEXT: (CURDIR)/MakefileTEXT:(MAKECMDGOALS)
意思就是所有不满足_all sub-make $(CURDIR)/Makefile这种模式的字符串,都被留下来,作为返回值。返回的结果,和_all 都依赖于sub_make。
⑥ 这一段是sub-make的规则。
ifeq ($(skip-makefile),)
后续的流程都是基于这个skip-makefile为空的,就一条条分开讲
MAKEFLAGS += --no-print-directory
–no-print-directory:不打印"Entering directory …"
ifeq ("$(origin C)", "command line")
KBUILD_CHECKSRC = $(C)
endif
ifndef KBUILD_CHECKSRC
KBUILD_CHECKSRC = 0
endif
检查命令行中有没有输入make C=XXX这种,如果有,把值付给KBUILD_CHECKSRC。
Shell命令 | KBUILD_CHECKSRC | Meaning |
---|---|---|
make | 0 | 不做源代码检查 |
make C=1 | 1 | 只检查重新编译过的源文件 |
make C=2 | 2 | 检查所有的源文件,无论他们会否重新编译过 |
ifdef SUBDIRS
KBUILD_EXTMOD ?= $(SUBDIRS)
endif
ifeq ("$(origin M)", "command line")
KBUILD_EXTMOD := $(M)
endif
此处针对的是编译外部模块:
PHONY += all
ifeq ($(KBUILD_EXTMOD),)
_all: all
else
_all: modules
endif
PHONY += all,这句比较诡异,既然所有的都要加all,为啥后面的_all又要单独分开,先持保留态度。
如果KBUILD_EXTMOD有值,就说明编译的是外部模块,_all 就依赖于modules,否则_all就依赖于all。
ifeq ($(KBUILD_SRC),)
# building in the source tree
srctree := .
else
ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
# building in a subdirectory of the source tree
srctree := ..
else
srctree := $(KBUILD_SRC)
endif
endif
objtree := .
src := $(srctree)
obj := $(objtree)
这三个变量后面用得到,src代表源代码路径,obj代表生成的目标文件路径。后面确定下是不是这个意思
。
VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
VPATH做啥用的,后面补充
export srctree objtree VPATH
导出这三个变量给下级子目录用。
SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
-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/ -e s/aarch64.*/arm64/ )
这条命令有点长,用shell执行的,如下拆开一一来分析下:
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-gnueabihf-
关键的东西来了,定义编译的体系架构和交叉编译工具链前缀。此处的?=,表示如果前面已经有赋值了,就用前面的赋值,如果没有,就用语句中的值来替代。比如make ARCH=X86 CROSS_COMPILE=xxx,那么此处的赋值语句就不会执行,还是用命令行中的值,只有make中没有指定,或者前面没有定义这两个变量,此处才有意义。
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),sparc32)
SRCARCH := sparc
endif
ifeq ($(ARCH),sparc64)
SRCARCH := sparc
endif
# Additional ARCH settings for sh
ifeq ($(ARCH),sh64)
SRCARCH := sh
endif
# Additional ARCH settings for tile
ifeq ($(ARCH),tilepro)
SRCARCH := tile
endif
ifeq ($(ARCH),tilegx)
SRCARCH := tile
endif
根据ARCH来确定SRCARCH。后面用到,看到了再补充。
# Where to locate arch specific headers
hdr-arch := $(SRCARCH)
hdr-arch赋值为SRCARCH。这里赋值符是:=,不同于上面的?=。:= 指的是不会使用后面的变量。也就是SRCARCH这里是啥,hdr-arch就是啥,不会因为你后面改变SRCARCH,而改变。
KCONFIG_CONFIG ?= .config
export KCONFIG_CONFIG
KCONFIG_CONFIG指的config的配置文件名称,此处指定为.config。并把KCONFIG_CONFIG导出给下级makefile使用。
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi ; fi)
这段语句,其实就是为了确定系统运行的bash是啥,一般都是/bin/bash。发现自己特别变态,喜欢追根究底这种奇怪的脚本。。。
-shell:指明用shell命令解析这句命令,这些都是bash的语法。
if [ -x "$$BASH" ]; then echo $$BASH;
这句话有点懵的,貌似$$BASH代表目前正在执行的bash的进程ID,if -x在bash中的意思是判断文件是否存在并且可执行。echo:bash的echo命令,就是输出的意思。就是判断现在是不是bash在执行咯,如果是,就输出它的ID给CONFIG_SHELL?试了下,没有走这分支。HOSTCC = gcc
HOSTCXX = g++
HOSTCFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu89
HOSTCXXFLAGS = -O2
保留,看后面怎么用这堆变量。
ifeq ($(shell $(HOSTCC) -v 2>&1 | grep -c "clang version"), 1)
HOSTCFLAGS += -Wno-unused-value -Wno-unused-parameter \
-Wno-missing-field-initializers -fno-delete-null-pointer-checks
endif
通过shell去判断HOSTCC的版本,然后添加HOSTCFLAGS 选项。
KBUILD_MODULES :=
KBUILD_BUILTIN := 1
确定是进行模块化编译还是内置编译。KBUILD_MODULES 模块化编译(modular),KBUILD_BUILTIN 内置编译(built-in),默认是内置编译。built-in就是将所有编译的东西都内置到内核中。模块化编译就是make modules。
ifeq ($(MAKECMDGOALS),modules)
KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1)
endif
如果我们使用命令make modules,就不会进行编译内置对象。如果使用modversions构建模块时,因为要考虑内置模块是不是最新的,所以这个变量就还是要置。(看注释写的,没用到过。)
ifneq ($(filter all _all modules,$(MAKECMDGOALS)),)
KBUILD_MODULES := 1
endif
用filter模式过滤,只要是make all/ make _all / make xxx modules之中之一,就要进行模块化编译。
ifeq ($(MAKECMDGOALS),)
KBUILD_MODULES := 1
endif
make为空这种,就是没有指定目标的,也要进行模块化编译,默认就是all嘛。
export KBUILD_MODULES KBUILD_BUILTIN
export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD
导出这一对给子makefile用。
ifneq ($(CC),)
ifeq ($(shell $(CC) -v 2>&1 | grep -c "clang version"), 1)
COMPILER := clang
else
COMPILER := gcc
endif
export COMPILER
endif
$CC如果不为空,通过版本判断编译器使用什么,这里一般都是gcc。Clang是一个C语言、C++、Objective-C语言的轻量级编译器,用的比较少,一般大家用GCC的多。把COMPILER导出给子makefile用。
scripts/Kbuild.include: ;
include scripts/Kbuild.include
包含scripts/Kbuild.include这个文件;
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
INSTALLKERNEL := installkernel
DEPMOD = /sbin/depmod
PERL = perl
PYTHON = python
CHECK = sparse
CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
-Wbitwise -Wno-return-void $(CF)
CFLAGS_MODULE =
AFLAGS_MODULE =
LDFLAGS_MODULE =
CFLAGS_KERNEL =
AFLAGS_KERNEL =
CFLAGS_GCOV = -fprofile-arcs -ftest-coverage
# Use USERINCLUDE when you must reference the UAPI directories only.
USERINCLUDE := \
-I$(srctree)/arch/$(hdr-arch)/include/uapi \
-Iarch/$(hdr-arch)/include/generated/uapi \
-I$(srctree)/include/uapi \
-Iinclude/generated/uapi \
-include $(srctree)/include/linux/kconfig.h
# Use LINUXINCLUDE when you must reference the include/ directory.
# Needed to be compatible with the O= option
LINUXINCLUDE := \
-I$(srctree)/arch/$(hdr-arch)/include \
-Iarch/$(hdr-arch)/include/generated/uapi \
-Iarch/$(hdr-arch)/include/generated \
$(if $(KBUILD_SRC), -I$(srctree)/include) \
-Iinclude \
$(USERINCLUDE)
KBUILD_CPPFLAGS := -D__KERNEL__
KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
-fno-strict-aliasing -fno-common \
-Werror-implicit-function-declaration \
-Wno-format-security \
-std=gnu89
KBUILD_AFLAGS_KERNEL :=
KBUILD_CFLAGS_KERNEL :=
KBUILD_AFLAGS := -D__ASSEMBLY__
KBUILD_AFLAGS_MODULE := -DMODULE
KBUILD_CFLAGS_MODULE := -DMODULE
KBUILD_LDFLAGS_MODULE := -T $(srctree)/scripts/module-common.lds
# Read KERNELRELEASE from include/config/kernel.release (if it exists)
KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)
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
export MAKE AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE
export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KASAN
export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
export KBUILD_ARFLAGS
# 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
# Files to ignore in find ... statements
export 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
PHONY += scripts_basic
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic ①
$(Q)rm -f .tmp_quiet_recordmcount ②
scripts/basic/%: scripts_basic ;
显示定义这条目标的规则,这个目录下所有的文件,都依赖scripts_basic
PHONY += outputmakefile ①
outputmakefile: ②
ifneq ($(KBUILD_SRC),) ③
$(Q)ln -fsn $(srctree) source ④
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL) ⑤
endif
这个目标的作用是如果指定了单独的输出路径,就在输出路径下产生一个makefile,有了makefile,在输出路径下如果执行make操作就很方便了,make最怕的就是没makefile。
① 目标加上outputmakefile
② outputmakefile规则如下:
③ 如果KBUILD_SRC不为空,则执行④ ⑤
④ @ln -fsn . source,ln是做软连接,语法为ln [参数][源文件或目录][目标文件或目录]
-f 强制执行
-s 软链接(符号链接)
-n 把符号链接视为一般目录
这里就是为当前目录建立一个source的符号链接
⑤ @/bin/bash ./scripts/mkmakefile . . 4 1 执行这个脚本,不深究了
PHONY += asm-generic ①
asm-generic: ②
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.asm-generic \
src=asm obj=arch/$(SRCARCH)/include/generated/asm ③
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.asm-generic \
src=uapi/asm obj=arch/$(SRCARCH)/include/generated/uapi/asm ④
汇编相关,支持在asm-generic使用通用的头。
① 目标加上asm-generic
② asm-generic规则:
③ @make -f ./scripts/Makefile.asm-generic src=asm obj=arch/arm/include/generated/asm
这条命令,最主要的目的是为了根据kbuild文件生成指定的头文件。make的时候,指定makefile为./scripts/Makefile.asm-generic,传入参数src和obj,所以有必要分析下./scripts/Makefile.asm-generic的内容。
#scripts/Makefile.asm-generic的内容如下:
kbuild-file := $(srctree)/arch/$(SRCARCH)/include/$(src)/Kbuild #line1
-include $(kbuild-file) #line2
include scripts/Kbuild.include #line3
# Create output directory if not already present
_dummy := $(shell [ -d $(obj) ] || mkdir -p $(obj)) #line4
quiet_cmd_wrap = WRAP $@ #line5
cmd_wrap = echo "\#include " >$@ #line6
all: $(patsubst %, $(obj)/%, $(generic-y)) #line7
@: #line8
$(obj)/%.h: #line9
$(call cmd,wrap) #line10
generic-y += bitsperlong.h
arch/arm/include/generated/asm
,所以输出目录其实就是此目录。bash语法 -d 就是判断一个目录是否存在,mkdir -p 则是级联创建此目录。这句没搞懂
"#include ,其实就是各自根据参数生成各自的头,举个例子,如果要生成的.h文件是bitsperlong.h
,那么最后在arch/arm/include/generated/asm
下就会出现一个bitsperlong.h,内容是#include
。最后结尾有个[>$@]
,这是bash的变量,>
是重定向符,$@
是所有参数的意思,所以>$@
就是重定向到参数的意思。。。这里没有理解为啥要重定向这个。
:
后面是他的依赖。这里用到了patsubst,模式字符串替换函数。$(patsubst ,, )
中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式
,如果匹配的话,则以
替换。这里,
可以包括通配符“%”,表示任意长度的字串。如果
中也包含“%”,那么,
中的这个“%”将是
中的那个“%”所代表的字串。
就是任意长度字符串,
就是变量generic-y
的值,而generic-y
来源于line1,由要生成的所有的.h
文件名组成,
就是$(obj)/%
,最后总的意思就是把由.h
文件名组成的字符串,每个.h
前面都加个arch/arm/include/generated/asm/
路径。$(obj)/%.h:
规则。version_h := include/generated/uapi/linux/version.h ①
old_version_h := include/linux/version.h ②
no-dot-config-targets := clean mrproper distclean \
cscope gtags TAGS tags help% %docs check% coccicheck \
$(version_h) headers_% archheaders archscripts \
kernelversion %src-pkg ③
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
ifeq ($(KBUILD_EXTMOD),) ⑩
ifneq ($(filter config %config,$(MAKECMDGOALS)),) ⑪
config-targets := 1 ⑫
ifneq ($(words $(MAKECMDGOALS)),1) ⑬
mixed-targets := 1 ⑭
endif
endif
endif
解决老版本的兼容问题。如果没有.config文件,而是config文件,比如make oldconfig all,这个时候就需要走此分支。
①、②:版本码。新版本中,include/linux/version.h可能不存在。
③:除去config之外的所有的target。打印出来后,clean mrproper distclean cscope gtags TAGS tags help% %docs check% coccicheck include/generated/uapi/linux/version.h headers_% archheaders archscripts kernelversion %src-pkg。
④:是否命令行中包含了config关键词,oldconfig,.config都算。默认为0,不包含config关键字。
⑤:混合方式,就是又有老的config,又有.config。默认为0,不混合。
⑥:只有.config,默认置为1,认为是只包含.config。
⑦:如果命令行中,包含了no-dot-config-targets③中的目标,只要有一个都算,走此分支,比如make proper,就会走此分支。
⑧:filterout去反过滤,就是命令行中,过滤掉no-dot-config-targets③中的目标后,为空,则走此分支,可以理解为,除了no-dot-config-targets,没有其他的target了,这个时候肯定就表示没有包含config关键字的target了。
⑨:dot-config置为0,表示没有config目标。
⑩:如果不编译外置模块,则走此分支。这个变量是表示外置模块编译的意思,之前有解释。
⑪:命令行中,包含了config或者%config的目标,走此分支。make config,make .config,make oldconfig,就都在此分支了。
⑫:config-targets置为1。
⑬:这里用到了words函数。判断是否只有1个单词,如果不只1个,就设定mixed-targets=1。
单词个数统计函数: $(words
)
功能: 统计字符串
中单词的个数
返回: 单词个数。
⑭:混合mixed-targets置为1。
ifeq ($(mixed-targets),1)
如果是混合模式,走此分支。
PHONY += $(MAKECMDGOALS) __build_one_by_one ①
$(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one ②
@: ③
__build_one_by_one: ④
$(Q)set -e; \ ⑤
for i in $(MAKECMDGOALS); do \ ⑥
$(MAKE) -f $(srctree)/Makefile $$i; \ ⑦
done
①、目标加上命令行中给的,和__build_one_by_one。
②、过滤出命令行中给的目标,除了__build_one_by_one以外的,让他们都依赖__build_one_by_one。
③、将其执行的命令行在执行前输出到屏幕上。
④、__build_one_by_one规则。
⑤、执行这段脚本文件有命令有异常则退出。
⑥、依次取MAKECMDGOALS中的目标。
⑦、每个目标,都执行@make -f ./Makefile $$i,$$i代表访问shell命令中定义的i变量。
else
下面就是非混合模式了。
ifeq ($(KBUILD_EXTMOD),) #line1
PHONY += scripts #line2
scripts: scripts_basic include/config/auto.conf include/config/tristate.conf \
asm-generic #line3
$(Q)$(MAKE) $(build)=$(@) #line4
# Objects we will link into vmlinux / subdirs we need to visit
init-y := init/ #line5
drivers-y := drivers/ sound/ firmware/ #line6
net-y := net/ #line7
libs-y := lib/ #line8
core-y := usr/ #line9
endif # KBUILD_EXTMOD
include scripts/Kbuild.include
,此文件中有句话,build := -f $(srctree)/scripts/Makefile.build obj
,就在那里赋值了。执行这句话的目的是,为了初始化很多kbuild文件需要的变量,还定义了很多的依赖规则,目标库名等等,反正做了挺多的。ifeq ($(dot-config),1) #line 1
-include include/config/auto.conf #line 2
ifeq ($(KBUILD_EXTMOD),) #line 3
-include include/config/auto.conf.cmd #line 4
$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ; #line 5
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd #line 6
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig #line 7
else #line 8
PHONY += include/config/auto.conf #line 9
include/config/auto.conf: #line 10
$(Q)test -e include/generated/autoconf.h -a -e $@ || ( \
echo >&2; \
echo >&2 " ERROR: Kernel configuration is invalid."; \
echo >&2 " include/generated/autoconf.h or $@ are missing.";\
echo >&2 " Run 'make oldconfig && make prepare' on kernel src to fix it."; \
echo >&2 ; \
/bin/false) #line 10
endif # KBUILD_EXTMOD
else #line 11
include/config/auto.conf: ; #line 12
endif # $(dot-config)
$(Q)test -e include/generated/autoconf.h -a -e $@
使用test -e这条shell命令,测试include/config/auto.conf文件是否存在,后面还接了-a -e 是把测试条件连接起来,判断include/config/auto.conf 和 include/config/auto.conf是否存在,-a -e都是判断文件是否存在不存在的。有一定优先级,具体啥优先级,没太清楚。总体意思是,只要有一个存在,结果就为真,继续往下。echo >&2;
是打印空行。/bin/false
啥都不干,设置退出码为1。all: vmlinux
all就是默认的target,如果make后什么都不带,就会执行all这个target了,他依赖于vmlinux,vmlinux很熟悉吧,就是我们最后生成的目标文件。
include arch/$(SRCARCH)/Makefile
包含arch/$(SRCARCH)/Makefile
,如果是arm,就include arch/arm/Makefile
。
KBUILD_CFLAGS += $(call cc-option,-fno-delete-null-pointer-checks,)
KBUILD_CFLAGS存放的是传递给编译器的编译选项,如果没有特殊要求,编译的文件被编译时用的就用的它。
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS += -Os $(call cc-disable-warning,maybe-uninitialized,)
else
KBUILD_CFLAGS += -O2
endif
编译优化选项,针对C的,CXX还有汇编应该都有另外的。如果没有定义宏CONFIG_CC_OPTIMIZE_FOR_SIZE,就用的-O2,目前用的多的就是这个。当然,加了优化,有些时候,编译器会给你自动优化,偶尔会出莫名的问题,这种就具体情况具体分析了,推荐还是二级优化。
# Tell gcc to never replace conditional load with a non-conditional one
KBUILD_CFLAGS += $(call cc-option,--param=allow-store-data-races=0)
继续加编译选项。这些也搞不清楚是写啥,直接看注释,告诉gcc不要用无条件加载替换条件加载。了解就好。
ifdef CONFIG_READABLE_ASM
# Disable optimizations that make assembler listings hard to read.
# reorder blocks reorders the control in the function
# ipa clone creates specialized cloned functions
# partial inlining inlines only parts of functions
KBUILD_CFLAGS += $(call cc-option,-fno-reorder-blocks,) \
$(call cc-option,-fno-ipa-cp-clone,) \
$(call cc-option,-fno-partial-inlining)
endif
ifneq ($(CONFIG_FRAME_WARN),0)
KBUILD_CFLAGS += $(call cc-option,-Wframe-larger-than=${CONFIG_FRAME_WARN})
endif
还要加编译选项。不去纠结了,直接翻译注释。针对汇编可读性和帧警告的。
ifdef CONFIG_CC_STACKPROTECTOR_REGULAR
stackp-flag := -fstack-protector
ifeq ($(call cc-option, $(stackp-flag)),)
$(warning Cannot use CONFIG_CC_STACKPROTECTOR_REGULAR: \
-fstack-protector not supported by compiler)
endif
else
ifdef CONFIG_CC_STACKPROTECTOR_STRONG
stackp-flag := -fstack-protector-strong
ifeq ($(call cc-option, $(stackp-flag)),)
$(warning Cannot use CONFIG_CC_STACKPROTECTOR_STRONG: \
-fstack-protector-strong not supported by compiler)
endif
else
# Force off for distro compilers that enable stack protector by default.
stackp-flag := $(call cc-option, -fno-stack-protector)
endif
endif
KBUILD_CFLAGS += $(stackp-flag)
处理帧保护器模式的编译选项。
ifeq ($(COMPILER),clang)
KBUILD_CPPFLAGS += $(call cc-option,-Qunused-arguments,)
KBUILD_CPPFLAGS += $(call cc-option,-Wno-unknown-warning-option,)
KBUILD_CFLAGS += $(call cc-disable-warning, unused-variable)
KBUILD_CFLAGS += $(call cc-disable-warning, format-invalid-specifier)
KBUILD_CFLAGS += $(call cc-disable-warning, gnu)
# Quiet clang warning: comparison of unsigned expression < 0 is always false
KBUILD_CFLAGS += $(call cc-disable-warning, tautological-compare)
# CLANG uses a _MergedGlobals as optimization, but this breaks modpost, as the
# source of a reference will be _MergedGlobals and not on of the whitelisted names.
# See modpost pattern 2
KBUILD_CFLAGS += $(call cc-option, -mno-global-merge,)
KBUILD_CFLAGS += $(call cc-option, -fcatch-undefined-behavior)
else
# This warning generated too much noise in a regular build.
# Use make W=1 to enable this warning (see scripts/Makefile.build)
KBUILD_CFLAGS += $(call cc-disable-warning, unused-but-set-variable)
endif
根据编译器,选择编译器选项,一般gcc。
ifdef CONFIG_FRAME_POINTER
KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
else
# Some targets (ARM with Thumb2, for example), can't be built with frame
# pointers. For those, we don't have FUNCTION_TRACER automatically
# select FRAME_POINTER. However, FUNCTION_TRACER adds -pg, and this is
# incompatible with -fomit-frame-pointer with current GCC, so we don't use
# -fomit-frame-pointer with FUNCTION_TRACER.
ifndef CONFIG_FUNCTION_TRACER
KBUILD_CFLAGS += -fomit-frame-pointer
endif
endif
帧指针相关。如果定义了宏CONFIG_FRAME_POINTER,走此分支。
KBUILD_CFLAGS += $(call cc-option, -fno-var-tracking-assignments)
-fvar-tracking-assignments:评注赋值以进行变量跟踪。加了no的就是不进行评注赋值了,不进行变量跟踪。
ifdef CONFIG_DEBUG_INFO
ifdef CONFIG_DEBUG_INFO_SPLIT
KBUILD_CFLAGS += $(call cc-option, -gsplit-dwarf, -g)
else
KBUILD_CFLAGS += -g
endif
KBUILD_AFLAGS += -Wa,-gdwarf-2
endif
如果定义了宏CONFIG_DEBUG_INFO。
ifdef CONFIG_DEBUG_INFO_DWARF4
KBUILD_CFLAGS += $(call cc-option, -gdwarf-4,)
endif
如果定义了宏CONFIG_DEBUG_INFO_DWARF4,走此分支。
ifdef CONFIG_DEBUG_INFO_REDUCED
KBUILD_CFLAGS += $(call cc-option, -femit-struct-debug-baseonly) \
$(call cc-option,-fno-var-tracking)
endif
如果定义了宏CONFIG_DEBUG_INFO_REDUCED,走此分支。
ifdef CONFIG_FUNCTION_TRACER
ifndef CC_FLAGS_FTRACE
CC_FLAGS_FTRACE := -pg
endif
export CC_FLAGS_FTRACE
ifdef CONFIG_HAVE_FENTRY
CC_USING_FENTRY := $(call cc-option, -mfentry -DCC_USING_FENTRY)
endif
KBUILD_CFLAGS += $(CC_FLAGS_FTRACE) $(CC_USING_FENTRY)
KBUILD_AFLAGS += $(CC_USING_FENTRY)
ifdef CONFIG_DYNAMIC_FTRACE
ifdef CONFIG_HAVE_C_RECORDMCOUNT
BUILD_C_RECORDMCOUNT := y
export BUILD_C_RECORDMCOUNT
endif
endif
endif
如果定义了宏CONFIG_FUNCTION_TRACER,走此分支。
ifdef CONFIG_DEBUG_SECTION_MISMATCH
KBUILD_CFLAGS += $(call cc-option, -fno-inline-functions-called-once)
endif
如果定义了宏CONFIG_DEBUG_SECTION_MISMATCH走此分支。
# 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)
CHECKFLAGS还不确定啥时候用。
# 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-disable-warning, pointer-sign)
# disable invalid "can't wrap" optimizations for signed / pointers
KBUILD_CFLAGS += $(call cc-option,-fno-strict-overflow)
# conserve stack if available
KBUILD_CFLAGS += $(call cc-option,-fconserve-stack)
# disallow errors like 'EXPORT_GPL(foo);' with missing header
KBUILD_CFLAGS += $(call cc-option,-Werror=implicit-int)
# require functions to have arguments in prototypes, not empty 'int foo()'
KBUILD_CFLAGS += $(call cc-option,-Werror=strict-prototypes)
# Prohibit date/time macros, which would make the build non-deterministic
KBUILD_CFLAGS += $(call cc-option,-Werror=date-time)
# use the deterministic mode of AR if available
KBUILD_ARFLAGS := $(call ar-option,D)
# check for 'asm goto'
ifeq ($(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-goto.sh $(CC)), y)
KBUILD_CFLAGS += -DCC_HAVE_ASM_GOTO
KBUILD_AFLAGS += -DCC_HAVE_ASM_GOTO
endif
这段是执行/bin/bash ./scripts/gcc-goto.sh arm-linux-gnueabihf-gcc。而gcc-got.sh是测试对’asm goto’ 是否支持。里头内容就自己去看了。
include scripts/Makefile.kasan
include scripts/Makefile.extrawarn
包含这两头文件,没去细看,后面补充。
# Add any arch overrides and user supplied CPPFLAGS, AFLAGS and CFLAGS as the
# last assignments
KBUILD_CPPFLAGS += $(ARCH_CPPFLAGS) $(KCPPFLAGS)
KBUILD_AFLAGS += $(ARCH_AFLAGS) $(KAFLAGS)
KBUILD_CFLAGS += $(ARCH_CFLAGS) $(KCFLAGS)
添加CXX,汇编,C的其他各种编译选项。
# Use --build-id when available.
LDFLAGS_BUILD_ID = $(patsubst -Wl$(comma)%,%,\
$(call cc-ldoption, -Wl$(comma)--build-id,))
KBUILD_LDFLAGS_MODULE += $(LDFLAGS_BUILD_ID)
LDFLAGS_vmlinux += $(LDFLAGS_BUILD_ID)
继续添加。不深究了。
ifeq ($(CONFIG_STRIP_ASM_SYMS),y)
LDFLAGS_vmlinux += $(call ld-option, -X,)
endif
如果定义了宏CONFIG_STRIP_ASM_SYMS,且值为y,LDFLAGS_vmlinux中添加-X,指定其后输入文件的语言。
export KBUILD_IMAGE ?= vmlinux
设置内核镜像的名字,并导出给下级makefile使用。
export INSTALL_PATH ?= /boot
镜像和系统映射文件的存放路径,默认为boot下。
export INSTALL_DTBS_PATH ?= $(INSTALL_PATH)/dtbs/$(KERNELRELEASE)
导出设备树的存放路径,并导出给下级makefile使用。这里输出后为/boot/dtbs/4.1.15+
。
MODLIB = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)
export MODLIB
导出MODLIB的存放路径,并导出给下级makefile使用。这里输出后为/lib/modules/4.1.15+
。
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
INSTALL_MOD_STRIP确定模块生成后是否需要被剥离。
mod_compress_cmd = true
ifdef CONFIG_MODULE_COMPRESS
ifdef CONFIG_MODULE_COMPRESS_GZIP
mod_compress_cmd = gzip -n
endif # CONFIG_MODULE_COMPRESS_GZIP
ifdef CONFIG_MODULE_COMPRESS_XZ
mod_compress_cmd = xz
endif # CONFIG_MODULE_COMPRESS_XZ
endif # CONFIG_MODULE_COMPRESS
export mod_compress_cmd
如果定义了CONFIG_MODULE_COMPRESS,则走此分支,确定模块被生成后是否需要被压缩。
# Select initial ramdisk compression format, default is gzip(1).
# This shall be used by the dracut(8) tool while creating an initramfs image.
#
INITRD_COMPRESS-y := gzip
INITRD_COMPRESS-$(CONFIG_RD_BZIP2) := bzip2
INITRD_COMPRESS-$(CONFIG_RD_LZMA) := lzma
INITRD_COMPRESS-$(CONFIG_RD_XZ) := xz
INITRD_COMPRESS-$(CONFIG_RD_LZO) := lzo
INITRD_COMPRESS-$(CONFIG_RD_LZ4) := lz4
确定初始ramdisk的压缩格式,默认是gzip。
ifdef CONFIG_MODULE_SIG_ALL
MODSECKEY = ./signing_key.priv
MODPUBKEY = ./signing_key.x509
export MODPUBKEY
mod_sign_cmd = perl $(srctree)/scripts/sign-file $(CONFIG_MODULE_SIG_HASH) $(MODSECKEY) $(MODPUBKEY)
else
mod_sign_cmd = true
endif
export mod_sign_cmd
CONFIG_MODULE_SIG_ALL不晓得做什么用。
ifeq ($(KBUILD_EXTMOD),)
下面是编译内置镜像时候,要走的分支。
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
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-alldirs := $(sort $(vmlinux-dirs) $(patsubst %/,%,$(filter %/, \
$(init-) $(core-) $(drivers-) $(net-) $(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)
vmlinux-dirs
最后的结果是init usr arch/arm/vfp arch/arm/vdso arch/arm/kernel arch/arm/mm arch/arm/common arch/arm/probes arch/arm/net arch/arm/crypto arch/arm/firmware arch/arm/mach-imx kernel mm fs ipc security crypto block drivers sound firmware net arch/arm/lib lib
。/
,最后调用sort函数,对留下的结果以及vmlinux-dirs合并后的字符串按首字母进行排序,排序的结果赋值给vmlinux-alldirs。打印最后的结果为:arch/arm/common arch/arm/crypto arch/arm/firmware arch/arm/kernel arch/arm/kvm arch/arm/lib arch/arm/mach-imx arch/arm/mm arch/arm/net arch/arm/nwfpe arch/arm/oprofile arch/arm/probes arch/arm/vdso arch/arm/vfp arch/arm/xen block crypto drivers firmware fs init ipc kernel lib mm net security sound usr
init/
,最后结果为:init/built-in.o
。# Externally visible symbols (used by link-vmlinux.sh)
export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y)
export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds
export LDFLAGS_vmlinux
导出下列变量给子makefile使用,有些直接把自己环境上的结果贴出来。
usr/built-in.o arch/arm/vfp/built-in.o arch/arm/vdso/built-in.o arch/arm/kernel/built-in.o arch/arm/mm/built-in.o arch/arm/common/built-in.o arch/arm/probes/built-in.o arch/arm/net/built-in.o arch/arm/crypto/built-in.o arch/arm/firmware/built-in.o arch/arm/mach-imx/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o drivers/built-in.o sound/built-in.o firmware/built-in.o net/built-in.o
arch/arm/kernel/vmlinux.lds
-p --no-undefined -X --pic-veneer --build-id
# used by scripts/pacmage/Makefile
export KBUILD_ALLDIRS := $(sort $(filter-out arch/%,$(vmlinux-alldirs)) arch Documentation include samples scripts tools virt)
看注释意思,给scripts/pacmage/Makefile
使用。先用filter-out,对变量vmlinux-alldirs的值,过滤出不满足模式arch/%,最后结果连同后面的arch Documentation …按首字母排序。最后结果为:Documentation arch block crypto drivers firmware fs include init ipc kernel lib mm net samples scripts security sound tools usr virt
vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)
不晓得这个变量做啥用的,直接把结果打印出来:arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o init/built-in.o usr/built-in.o arch/arm/vfp/built-in.o arch/arm/vdso/built-in.o arch/arm/kernel/built-in.o arch/arm/mm/built-in.o arch/arm/common/built-in.o arch/arm/probes/built-in.o arch/arm/net/built-in.o arch/arm/crypto/built-in.o arch/arm/firmware/built-in.o arch/arm/mach-imx/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o drivers/built-in.o sound/built-in.o firmware/built-in.o net/built-in.o
# Final link of vmlinux
cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)
CONFIG_SHELL:/bin/bash,前面已经确定了这个变量的值。
$<:makefile中依赖文件集合的第一个,这里没有,所以是空。
LD:arm-linux-gnueabihf-ld ,前面确定了这个变量的值。
LDFLAGS:前面确定了这个变量的值
/bin/bash arm-linux-gnueabihf-ld -EL -p --no-undefined -X --pic-veneer --build-id
quiet_cmd_link-vmlinux = LINK $@
$@:规则中目标集合。这里如果没有,就直接是LINK。这个变量还不确定怎么用,先留悬念。
# Include targets which we want to
# execute if the rest of the kernel build went well.
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
重头戏来了,vmlinux,终极大boss。依赖scripts/link-vmlinux.sh脚本,$(vmlinux-deps)。加FORCE参数,不管目标在不在,依赖是否更新,都重新生成目标以及依赖中隐含的目标。
ifdef CONFIG_HEADERS_CHECK
$(Q)$(MAKE) -f $(srctree)/Makefile headers_check
endif
如果定义了宏CONFIG_HEADERS_CHECK,就执行下面的命令进行头部检查:@make -f ./Makefile headers_check
ifdef CONFIG_SAMPLES
$(Q)$(MAKE) $(build)=samples
endif
如果定义了宏CONFIG_SAMPLES,就执行下面的命令:@make -f ./scripts/Makefile.build obj=samples
,这里不是很清楚在做什么。
ifdef CONFIG_BUILD_DOCSRC
$(Q)$(MAKE) $(build)=Documentation
endif
如果定义了宏CONFIG_BUILD_DOCSRC,就执行下面的命令:@make -f ./scripts/Makefile.build obj=Documentation
,这里不是很清楚在做什么。
ifdef CONFIG_GDB_SCRIPTS
$(Q)ln -fsn `cd $(srctree) && /bin/pwd`/scripts/gdb/vmlinux-gdb.py
endif
@ln -fsn /home/chen/tbox/linux-imx_4.1.15_2.1.0/scripts/gdb/vmlinux-gdb.py,这个创建软链接没有目标文件,疑问。这里的目的也没理清楚。
+$(call if_changed,link-vmlinux)
执行cmd_link-vmlinux,这个变量上面有定义过,做链接操作。
$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;
排序后,指定依赖关系。
PHONY += $(vmlinux-dirs)
$(vmlinux-dirs): prepare scripts
$(Q)$(MAKE) $(build)=$@
依次处理各级子目录,结果如下:
@make -f ./scripts/Makefile.build obj=usr
@make -f ./scripts/Makefile.build obj=arch/arm/vfp
@make -f ./scripts/Makefile.build obj=arch/arm/vdso
@make -f ./scripts/Makefile.build obj=arch/arm/kernel
@make -f ./scripts/Makefile.build obj=arch/arm/mm
@make -f ./scripts/Makefile.build obj=arch/arm/common
@make -f ./scripts/Makefile.build obj=arch/arm/probes
@make -f ./scripts/Makefile.build obj=arch/arm/net
@make -f ./scripts/Makefile.build obj=arch/arm/crypto
@make -f ./scripts/Makefile.build obj=arch/arm/firmware
@make -f ./scripts/Makefile.build obj=arch/arm/mach-imx
@make -f ./scripts/Makefile.build obj=kernel
@make -f ./scripts/Makefile.build obj=mm
@make -f ./scripts/Makefile.build obj=fs
@make -f ./scripts/Makefile.build obj=ipc
@make -f ./scripts/Makefile.build obj=security
@make -f ./scripts/Makefile.build obj=crypto
@make -f ./scripts/Makefile.build obj=block
@make -f ./scripts/Makefile.build obj=drivers
@make -f ./scripts/Makefile.build obj=sound
@make -f ./scripts/Makefile.build obj=firmware
@make -f ./scripts/Makefile.build obj=net
@make -f ./scripts/Makefile.build obj=arch/arm/lib
@make -f ./scripts/Makefile.build obj=lib
define filechk_kernel.release
echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))"
endef
定义了这个函数,主要是输出kernel version,下面就会用到。
# Store (new) KERNELRELEASE string in include/config/kernel.release
include/config/kernel.release: include/config/auto.conf FORCE
$(call filechk,kernel.release)
调用filechk_kernel.release。
PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3
目标加上这部分,prepareN在prepareN-1之前执行。
# 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)
prepare3: include/config/kernel.release #line 1
ifneq ($(KBUILD_SRC),) #line 2
@$(kecho) ' Using $(srctree) as source for kernel' #line 3
$(Q)if [ -f $(srctree)/.config -o -d $(srctree)/include/config ]; then \
echo >&2 " $(srctree) is not clean, please run 'make mrproper'"; \
echo >&2 " in the '$(srctree)' directory.";\
/bin/false; \
fi; #line 4
endif
# prepare2 creates a makefile if using a separate output directory
prepare2: prepare3 outputmakefile asm-generic
定义prepare2的依赖规则。
prepare1: prepare2 $(version_h) include/generated/utsrelease.h \
include/config/auto.conf
$(cmd_crmodverdir)
定义prepare1的依赖规则,执行cmd_crmodverdir。这个变量在后面定义。
archprepare: archheaders archscripts prepare1 scripts_basic
定义archprepare的依赖规则。
prepare0: archprepare FORCE
$(Q)$(MAKE) $(build)=.
定义prepare0的依赖规则,执行@make -f ./scripts/Makefile.build obj=.。
prepare: prepare0
定义prepare0的依赖规则。
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; \
(echo \#define UTS_RELEASE \"$(KERNELRELEASE)\";)
endef
函数功能:判断内核版本号字符数目是否合法。
include/config/kernel.release
读出的,kernel的release版本号,我的环境是4.1.15+。 define filechk_version.h
(echo \#define LINUX_VERSION_CODE $(shell \
expr $(VERSION) \* 65536 + 0$(PATCHLEVEL) \* 256 + 0$(SUBLEVEL)); \ ①
echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))';) ②
endef
函数功能:定义了版本码,并提供KERNEL_VERSION宏接口。
$(version_h): $(srctree)/Makefile FORCE ①
$(call filechk,version.h) ②
$(Q)rm -f $(old_version_h) ③
①:定义version_h的依赖规则。version_h
变量前面有定义,值是:include/generated/uapi/linux/version.h
。
②:调用filechk_version.h,就是刚才前面定义的函数。
③:old_version_h也是前面定义的,这句命令就是@rm -f include/linux/version.h
。
include/generated/utsrelease.h: include/config/kernel.release FORCE ①
$(call filechk,utsrelease.h) ②
①:定义依赖关系
②:调用filechk_utsrelease.h
PHONY += headerdep ①
headerdep:
$(Q)find $(srctree)/include/ -name '*.h' | xargs --max-args 1 \
$(srctree)/scripts/headerdep.pl -I$(srctree)/include ②
此规则的意思,就是调用scripts/headerdep.pl
去处理./include/*.h
文件。
①:目标加上headerdep。
②:定义规则。查找./include/
目录下,文件名后缀是.h
结尾的;xargs
:xargs 是一个强有力的命令,它能够捕获一个命令的输出,然后传递给另外一个命令。这里就是将查询到的结果,传递给下一个命令,也就是传给perl脚本。--max-args 1
:指命令参数最多为1,限定的是后面的perl脚本,看起来像是用脚本一个一个处理。这个perl脚本做啥用的,真没看懂,不是很懂perl,后续返回来补充。
# 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 ⑥
①:定义INSTALL_FW_PATH,在我环境上,INSTALL_FW_PATH=/lib/firmware。
②:导出INSTALL_FW_PATH给下级子目录用。
③:目标加上firmware_install。
④:定义firmware_install依赖规则,不依赖任何,但是加了FORCE关键字,无论目标是否存在或者有更新,都重新生成。
⑤:级联创建$(objtree)/firmware
目录。
⑥:调用make命令,指定makefile为$(srctree)/scripts/Makefile.fwinst
,obj=firmware __fw_install
#Default location for installed headers
export INSTALL_HDR_PATH = $(objtree)/usr
导出INSTALL_HDR_PATH 给下级makefile用,INSTALL_HDR_PATH 是默认安装header的路径。
# 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)
$(if , )
或者$(if ,, )
。
参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,
会被计算,否则
会被计算。
为真(非空字符串),那个
会是整个函数的返回值。
为假(空字符串),那么
会是整个函数的返回值,此时如果
没有被定义,那么,整个函数返回空字串。KBUILD_HEADERS
是否有值,给dst
变量赋值。hdr-arch
前面有被赋值,等于SRCARCH
,而SRCARCH
= $(ARCH)
,在我的环境下就是arm。所以,总结下来就是KBUILD_HEADERS
非空,dst=include/asm-$(hdr-arch)
,如果为空,dst=include/asm)
。KBUILD_HEADERS
赋值。PHONY += archheaders
archheaders:
PHONY += archscripts
archscripts:
定义两个空规则,俺也不晓得为啥。。。。
PHONY += __headers ①
__headers: $(version_h) scripts_basic asm-generic archheaders archscripts FORCE ②
$(Q)$(MAKE) $(build)=scripts build_unifdef ③
①:目标加上__headers
。
②:定义__headers的依赖。
③:@make -f ./scripts/Makefile.build obj=scripts build_unifdef
PHONY += headers_install_all ①
headers_install_all: ②
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/headers.sh install ③
①:加目标。
②:定义依赖关系。
③:@/bin/bash ./scripts/headers.sh install。作用后面补。
PHONY += headers_install ①
headers_install: __headers ②
$(if $(wildcard $(srctree)/arch/$(hdr-arch)/include/uapi/asm/Kbuild),, \
$(error Headers not exportable for the $(SRCARCH) architecture)) ③
$(Q)$(MAKE) $(hdr-inst)=include/uapi ④
$(Q)$(MAKE) $(hdr-inst)=arch/$(hdr-arch)/include/uapi/asm $(hdr-dst) ⑤
①:加目标
②:定依赖关系
③:wildcard
是扩展通配符函数。语法:$(wildcard PATTERN...)
。此处先把$(srctree)/arch/$(hdr-arch)/include/uapi/asm/Kbuild)
展开,如果不为空,啥都不干,如果为空,输出错误。纳闷,怎么就可以为空,没想通这种场景。
④:@make -f ./scripts/Makefile.headersinst obj=include/uapi。作用后面补充。
⑤:@make -f ./scripts/Makefile.headersinst obj=arch/arm/include/uapi/asm dst=include/asm。里头的两变量,前面都定义了。
PHONY += headers_check_all
headers_check_all: headers_install_all
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/headers.sh check ①
①:@/bin/bash ./scripts/headers.sh check。作用后面补。
PHONY += headers_check
headers_check: headers_install
$(Q)$(MAKE) $(hdr-inst)=include/uapi HDRCHECK=1 ①
$(Q)$(MAKE) $(hdr-inst)=arch/$(hdr-arch)/include/uapi/asm $(hdr-dst) HDRCHECK=1 ②
①、@make -f ./scripts/Makefile.headersinst obj=include/uapi HDRCHECK=1
②、@make -f ./scripts/Makefile.headersinst obj=arch/arm/include/uapi/asm dst=include/asm HDRCHECK=1
PHONY += kselftest
kselftest:
$(Q)$(MAKE) -C tools/testing/selftests run_tests
@make -C tools/testing/selftests run_tests,内核自检。
ifdef CONFIG_MODULES
编译modules。这个分支,默认是会进的,但是我还没有找到哪里定义的CONFIG_MODULES
,后面返回来补充。fix me
all: modules
定义依赖规则。
PHONY += modules
modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) modules.builtin ①
$(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 ⑤
①:定义依赖关系,不过其中有一个依赖要根据宏KBUILD_BUILTIN
是否定义决定,如果定义了,就依赖vmlinux。
②:这个看着有点脑壳皮痛。一点点来。
awk
。awk是一个强大的文本分析工具,简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。$(vmlinux-dirs:%=$(objtree)/%/modules.order)
:这句话是把vmlinux-dirs
中的字符串,模式替换为$(objtree)/%/modules.order
,比如usr
,就变成./usr/modules.order
,依此内推,把里头的字符串全部按此规则替换掉。awk
可以用循环,估计是依次推断读出来的内容,是否已经存在,如果不存在,才继续做下面操作,这样可以去除重复的内容。vmlinux-dirs
的值依次进行替换,然后替换后的值其实就是一个文件的名字,比如./usr/modules.order
就是一个文件名,然后依次读取此文件中的内容,每行用空格分开,判断这些按行读取的内容是否已经出现过,如果没有出现,就将值输出到$(objtree)/modules.order
中去。kecho := $($(quiet)kecho)
,变量quiet=quiet_,所以kecho=quiet_kecho,而quiet_kecho在kbuild.include里头定义为了echo,quiet_kecho := echo
,所以就是输出字符串Building modules, stage 2.
。modules.builtin: $(vmlinux-dirs:%=%/modules.builtin) ①
$(Q)$(AWK) '!x[$$0]++' $^ > $(objtree)/modules.builtin ②
①:定义依赖规则。依赖的目标,由$(vmlinux-dirs)
变成了将$(vmlinux-dirs)
中的字符串模式替换为%/modules.builtin
后的字符串。
②:awk又粉墨登场了。$^
表示所有依赖的集合,就是①中模式替换后的字符串,替换后的字符串其实都是文件名,逐行读取那些文件的内容,去除重复的,输出到$(objtree)/modules.builtin
里。
%/modules.builtin: include/config/auto.conf ①
$(Q)$(MAKE) $(modbuiltin)=$* ②
①:定义%/modules.builtin
的依赖关系。
②:@*:表示目标模式中%及其之前的部分。modbuiltin = -f ./scripts/Makefile.modbuiltin obj
最终的结果如下:
@make -f ./scripts/Makefile.modbuiltin obj=$*
。结合上下文,$*
其实就是$(vmlinux-dirs)
。先模式替换给他加上的这个后缀嘛。
# 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_:
@rm -rf $(MODLIB)/kernel
@rm -f $(MODLIB)/source
@mkdir -p $(MODLIB)/kernel
@ln -s `cd $(srctree) && /bin/pwd` $(MODLIB)/source
@if [ ! $(objtree) -ef $(MODLIB)/build ]; then \
rm -f $(MODLIB)/build ; \
ln -s $(CURDIR) $(MODLIB)/build ; \
fi
@cp -f $(objtree)/modules.order $(MODLIB)/
@cp -f $(objtree)/modules.builtin $(MODLIB)/
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modinst
这里都是删建目录,做软链接和拷贝工作,最后执行了一个make。
PHONY += _modinst_post
_modinst_post: _modinst_
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modinst
$(call cmd,depmod)
ifeq ($(CONFIG_MODULE_SIG), y)
PHONY += modules_sign
modules_sign:
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modsign
endif
else # CONFIG_MODULES
# Modules not configured
# ---------------------------------------------------------------------------
modules modules_install: FORCE
@echo >&2
@echo >&2 "The present kernel configuration has modules disabled."
@echo >&2 "Type 'make config' and enable loadable module support."
@echo >&2 "Then build a kernel with module support enabled."
@echo >&2
@exit 1
endif # CONFIG_MODULES
只管打印,啥都没干。
###
# Cleaning is done on three levels.
# 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)
# Directories & files removed with 'make mrproper'
MRPROPER_DIRS += include/config usr/include include/generated \
arch/*/include/generated .tmp_objdiff
MRPROPER_FILES += .config .config.old .version .old_version \
Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \
signing_key.priv signing_key.x509 x509.genkey \
extra_certificates signing_key.x509.keyid \
signing_key.x509.signer vmlinux-gdb.py
一堆变量赋值,定义各种目录和文件夹或者文件名。
clean: rm-dirs := $(CLEAN_DIRS) ①
clean: rm-files := $(CLEAN_FILES) ②
clean-dirs := $(addprefix _clean_, . $(vmlinux-alldirs) Documentation samples) ③
①、②:定义clean的依赖规则,并给变量赋值。用:=
会比较诡异,因为后面如果$开头的变量,后面值发生变化,是以最后一次变量值为准。
③:addprefix用于添加前缀,添加的目标是$(vmlinux-alldirs) Documentation samples)
格式为:$(addprefix fixstring,string1 string2 …)。clean-dirs
最后的结果就是:
clean. _clean_arch/arm/common _clean_arch/arm/crypto _clean_arch/arm/firmware _clean_arch/arm/kernel _clean_arch/arm/kvm _clean_arch/arm/lib _clean_arch/arm/mach-imx _clean_arch/arm/mm _clean_arch/arm/net _clean_arch/arm/nwfpe _clean_arch/arm/oprofile _clean_arch/arm/probes _clean_arch/arm/vdso _clean_arch/arm/vfp _clean_arch/arm/xen _clean_block _clean_crypto _clean_drivers _clean_firmware _clean_fs _clean_init _clean_ipc _clean_kernel _clean_lib _clean_mm _clean_net _clean_security _clean_sound _clean_usr _clean_Documentation _clean_samples
PHONY += $(clean-dirs) clean archclean vmlinuxclean
$(clean-dirs):
$(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) ①
① 定义依赖规则:
$(patsubst _clean_%,%,$@)
:patsubst 是字符串替换函数,前面说过了。$@
目标的集合。意思就是把目标集合$(clean-dirs)
中字符串前面的_clean_
去除。最后执行的就是:@make -f ./scripts/Makefile.clean obj=$(vmlinux-alldirs) Documentation samples)
vmlinuxclean:
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/link-vmlinux.sh clean
@/bin/bash ./scripts/link-vmlinux.sh clean:调用这个shell脚本去清理。详情自己去看。
clean: archclean vmlinuxclean
依赖关系来了。
# 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_%,%,$@)
和上面的clean-dirs类似,不累赘了。
mrproper: clean archmrproper $(mrproper-dirs)
$(call cmd,rmdirs)
$(call cmd,rmfiles)
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 -name '*%' -o -name 'core' \) \
-type f -print | xargs rm -f ②
distclean来了。
①:定义依赖关系。
②:用find命令去$(srctree)下查找。
export 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
find /home/student -path /home/student/sep -prune -o -name "tmp.txt" -print
# rpm target kept for backward compatibility
package-dir := scripts/package
%src-pkg: FORCE
$(Q)$(MAKE) $(build)=$(package-dir) $@
%pkg: include/config/kernel.release FORCE
$(Q)$(MAKE) $(build)=$(package-dir) $@
rpm: include/config/kernel.release FORCE
$(Q)$(MAKE) $(build)=$(package-dir) $@
注释写了,给rpm目标留着的。
boards := $(wildcard $(srctree)/arch/$(SRCARCH)/configs/*_defconfig)
boards := $(sort $(notdir $(boards)))
board-dirs := $(dir $(wildcard $(srctree)/arch/$(SRCARCH)/configs/*/*_defconfig))
board-dirs := $(sort $(notdir $(board-dirs:/=)))
help:
...
$(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)=scripts build_docproc
$(Q)$(MAKE) $(build)=Documentation/DocBook $@
一堆help和doc的,就不写了。
else # KBUILD_EXTMOD
# make M=dir clean Delete all automatically generated files
# make M=dir modules Make all modules in specified dir
# make M=dir Same as 'make M=dir modules'
# make M=dir modules_install
# Install the modules built in the module directory
# Assumes install directory is already created
# We are always building modules
KBUILD_MODULES := 1
PHONY += crmodverdir
crmodverdir:
$(cmd_crmodverdir) ①
编译外部模块。只有以上几种合法的语法。
①:在下面定义了:cmd_crmodverdir = $(Q)mkdir -p $(MODVERDIR)
$(if $(KBUILD_MODULES),; rm -f $(MODVERDIR)/*)
PHONY += $(objtree)/Module.symvers
$(objtree)/Module.symvers:
@test -e $(objtree)/Module.symvers || ( \
echo; \
echo " WARNING: Symbol version dump $(objtree)/Module.symvers"; \
echo " is missing; modules will have no dependencies and modversions."; \
echo )
module-dirs := $(addprefix _module_,$(KBUILD_EXTMOD))
PHONY += $(module-dirs) modules
$(module-dirs): crmodverdir $(objtree)/Module.symvers
$(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@)
modules: $(module-dirs)
@$(kecho) ' Building modules, stage 2.';
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
PHONY += modules_install
modules_install: _emodinst_ _emodinst_post
install-dir := $(if $(INSTALL_MOD_DIR),$(INSTALL_MOD_DIR),extra)
PHONY += _emodinst_
_emodinst_:
$(Q)mkdir -p $(MODLIB)/$(install-dir)
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modinst
PHONY += _emodinst_post
_emodinst_post: _emodinst_
$(call cmd,depmod)
clean-dirs := $(addprefix _clean_,$(KBUILD_EXTMOD))
PHONY += $(clean-dirs) clean
$(clean-dirs):
$(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@)
clean: rm-dirs := $(MODVERDIR)
clean: rm-files := $(KBUILD_EXTMOD)/Module.symvers
没有啥特殊的,就不拎出来写了。
help:
@echo ' Building external modules.'
@echo ' Syntax: make -C path/to/kernel/src M=$$PWD target'
@echo ''
@echo ' modules - default target, build the module(s)'
@echo ' modules_install - install the module'
@echo ' clean - remove generated files in module directory only'
@echo ''
# Dummies...
PHONY += prepare scripts
prepare: ;
scripts: ;
endif # KBUILD_EXTMOD
clean: $(clean-dirs)
$(call cmd,rmdirs)
$(call cmd,rmfiles)
@find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \
\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
-o -name '*.ko.*' \
-o -name '*.dwo' \
-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
-o -name '*.symtypes' -o -name 'modules.order' \
-o -name modules.builtin -o -name '.tmp_*.o.*' \
-o -name '*.gcno' \) -type f -print | xargs rm -f
quiet_cmd_tags = GEN $@
cmd_tags = $(CONFIG_SHELL) $(srctree)/scripts/tags.sh $@
tags TAGS cscope gtags: FORCE
$(call cmd,tags)
调脚本生成tag。
PHONY += includecheck versioncheck coccicheck namespacecheck export_report
includecheck:
find $(srctree)/* $(RCS_FIND_IGNORE) \
-name '*.[hcS]' -type f -print | sort \
| xargs $(PERL) -w $(srctree)/scripts/checkincludes.pl
versioncheck:
find $(srctree)/* $(RCS_FIND_IGNORE) \
-name '*.[hcS]' -type f -print | sort \
| xargs $(PERL) -w $(srctree)/scripts/checkversion.pl
coccicheck:
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/$@
namespacecheck:
$(PERL) $(srctree)/scripts/namespace.pl
export_report:
$(PERL) $(srctree)/scripts/export_report.pl
endif #ifeq ($(config-targets),1)
endif #ifeq ($(mixed-targets),1)
一大堆一致性检查。
PHONY += checkstack kernelrelease kernelversion image_name
# UML needs a little special treatment here. It wants to use the host
# toolchain, so needs $(SUBARCH) passed to checkstack.pl. Everyone
# else wants $(ARCH), including people doing cross-builds, which means
# that $(SUBARCH) doesn't work here.
ifeq ($(ARCH), um)
CHECKSTACK_ARCH := $(SUBARCH)
else
CHECKSTACK_ARCH := $(ARCH)
endif
checkstack:
$(OBJDUMP) -d vmlinux $$(find . -name '*.ko') | \
$(PERL) $(src)/scripts/checkstack.pl $(CHECKSTACK_ARCH)
kernelrelease:
@echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))"
kernelversion:
@echo $(KERNELVERSION)
image_name:
@echo $(KBUILD_IMAGE)
发布和打印。
# Clear a bunch of variables before executing the submake
tools/: FORCE
$(Q)mkdir -p $(objtree)/tools
$(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(objtree) subdir=tools -C $(src)/tools/
tools/%: FORCE
$(Q)mkdir -p $(objtree)/tools
$(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(objtree) subdir=tools -C $(src)/tools/ $*
???
# Single targets are compatible with:
# - build with mixed source and output
# - build with separate output dir 'make O=...'
# - external modules
#
# target-dir => where to store outputfile
# build-dir => directory in kernel source tree to use
ifeq ($(KBUILD_EXTMOD),)
build-dir = $(patsubst %/,%,$(dir $@))
target-dir = $(dir $@)
else
zap-slash=$(filter-out .,$(patsubst %/,%,$(dir $@)))
build-dir = $(KBUILD_EXTMOD)$(if $(zap-slash),/$(zap-slash))
target-dir = $(if $(KBUILD_EXTMOD),$(dir $<),$(dir $@))
endif
针对单独目标的操作,比如make O=xxx
%.s: %.c prepare scripts FORCE
$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
%.i: %.c prepare scripts FORCE
$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
%.o: %.c prepare scripts FORCE
$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
%.lst: %.c prepare scripts FORCE
$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
%.s: %.S prepare scripts FORCE
$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
%.o: %.S prepare scripts FORCE
$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
%.symtypes: %.c prepare scripts FORCE
$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
# Modules
/: prepare scripts FORCE
$(cmd_crmodverdir)
$(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
$(build)=$(build-dir)
# Make sure the latest headers are built for Documentation
Documentation/: headers_install
%/: prepare scripts FORCE
$(cmd_crmodverdir)
$(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
$(build)=$(build-dir)
%.ko: prepare scripts FORCE
$(cmd_crmodverdir)
$(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
$(build)=$(build-dir) $(@:.ko=.o)
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
规则来咯。
quiet_cmd_rmdirs = $(if $(wildcard $(rm-dirs)),CLEAN $(wildcard $(rm-dirs)))
cmd_rmdirs = rm -rf $(rm-dirs)
quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN $(wildcard $(rm-files)))
cmd_rmfiles = rm -f $(rm-files)
# Run depmod only if we have System.map and depmod is executable
quiet_cmd_depmod = DEPMOD $(KERNELRELEASE)
cmd_depmod = $(CONFIG_SHELL) $(srctree)/scripts/depmod.sh $(DEPMOD) \
$(KERNELRELEASE) "$(patsubst y,_,$(CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX))"
# Create temporary dir for module support files
# clean it up only when building all modules
cmd_crmodverdir = $(Q)mkdir -p $(MODVERDIR) \
$(if $(KBUILD_MODULES),; rm -f $(MODVERDIR)/*)
这些前面用到了。
# read all saved command lines
targets := $(wildcard $(sort $(targets))) ①
cmd_files := $(wildcard .*.cmd $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) ②
①、从命令行读取有的目标,排序,展开
②对于targets中每个字符串,处理成【目录.文件名.cmd】,联合【*.cmd】,全部展开。
$(foreach ,,)
;中的单词逐一取出放到参数
;所指定的变量中,然后再执行< text>;所包含的表达式。每一次
;会返回一个字符串,循环过程中,
;的所返回的每个字符串会以空格分隔,最后当整个循环结束时,
;所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。
中取出目录部分
中的目录部分
中取出非目录部分
中的非目录部分ifneq ($(cmd_files),)
$(cmd_files): ; # Do not try to update included dependency files
include $(cmd_files)
endif
如果不为空,加头文件。
endif # skip-makefile
结束了。
PHONY += FORCE
FORCE:
# Declare the contents of the .PHONY variable as phony. We keep that
# information in a variable so we can use it in if_changed and friends.
.PHONY: $(PHONY)
最终的.PHONY 依赖PHONY