本文是最近学习Linux Makefile的一些内容,Linux版本号为2.6.32。Makefile内容较多,分成了几个阶段。这里写下来,作为积累,
也想和大家分享下学习过程。
==============================================================================
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 22
EXTRAVERSION = .6
NAME = Holy Dancing Manatees, Batman!
# *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.
# *文档*
# 执行“make help”可以查看典型的目标
# 更多信息可以在./README中查看
# 这个文件的内容只适合不期望知道怎么构建内核的开发者来阅读。
# Do not:
# o use make’s built-in rules and variables
# (this increases performance and avoid hard-to-debug behavour);
# o print “Entering directory …”;
# 不做哪些: ps:下面列出的都是makefile做的事情
# o 使用内嵌规则和变量
(这样做可以提高性能,同时便于调试)
# o 打印“Entering directory …”
MAKEFLAGS += -rR –no-print-directory r 取消所有内嵌的隐含规则
R 取消 make 内嵌的隐含变量
–no-print-directory 在 make 进入一个目录读取 Makefile 之前打印工作目录
# 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.
# 我们使用递归的形式构建,所以我们需要花些心思了解权限职责问题。
#
# 最主要的:子Makefiles应该只修改它们自己目录下的文件。如果在某个目录下,我们依赖另一个目录下的文件
# (这种情况不经常发生,但当链接built-in.o文件时,这是不可避免的,built-in.o文件最终链接进vmlinux),
# 我们将调用另一个目录下的子make,在那之后,我们确定在这个目录下的所有文件都是最新的。
#
# 唯一的情况我们需要修改会造成全局影响的文件,这种情况被分离出来,在递归开始前完成。他们被作为预备规
# 则明确列出来了。
# To put more focus on warnings, be less verbose as default
# Use ‘make V=1’ to see the full commands
# 为了专注于警告信息,默认信息为非冗余的。
# 用‘make V=1’可以看完整的命令。
ifdef V
ifeq (“$(origin V)”, “command line”) //判断V是否为命令行参数
KBUILD_VERBOSE = $(V)
endif
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif
# 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.
# 在C编译时使用代码检测工具(默认为“sparse”) sparse是由linux之父开发的, 目的就是提供一个静态检查
# 代码的工具, 从而减少linux内核的隐患
#
# 使用‘make C=1’使能仅检测预编译文件。
# 使用‘make C=2’使能检测所有文件,不论是不是预编译文件
#
# “Documentation/sparse.txt”中有更详细的描述,包括如何获取“sparse”效用。
ifdef C
ifeq (“$(origin C)”, “command line”)
KBUILD_CHECKSRC = $(C)
endif
endif
ifndef KBUILD_CHECKSRC
KBUILD_CHECKSRC = 0
endif
# 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
# 使用“make M=dir”来指明所要构建的外部模块目录。
# 旧的语法“make … SUBDIRS=$PWD”仍然被支持
# 设置环境变量KBUILD_EXTMOD优先级更高。
ifdef SUBDIRS
KBUILD_EXTMOD ?= $(SUBDIRS) //KBUILD_EXTMOD没有定义时等于$(SUBDIRS)
endif
ifeq (“$(origin M)”, “command line”)
KBUILD_EXTMOD := $(M)
endif
# 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.
# kbuild支持将输出文件保存在单独的目录下。
# 将输出文件防止在单独的目录下有两种语法被支持。
# 两种方法的工作目录必须是内核源码的根目录。
# 1) O=
# 使用”make O=dir/to/store/output/files/”
#
# 2) 设置KBUILD_OUTPUT变量
# 设置KBUILD_OUTPUT环境变量去指明输出文件放置的目录。
# export KBUILD_OUTPUT=dir/to/store/output/files/
# make
#
# “O=”的方式比设置KBUILD_OUTPUT环境变量更优先考虑。
# 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)
# KBUILD_SRC在OBJ目录下调用make时设置的。
# KBUILD_SRC现在不是为了让一般用户使用的。
ifeq ($(KBUILD_SRC),)
# OK, Make called in directory where kernel src resides
# Do we want to locate output files in a separate directory?
# Make在内核源文件目录下调用。
# 我们是否想将输出文件放置到独立的目录中?
ifeq (“$(origin O)”, “command line”) //判断O是否为命令行参数
KBUILD_OUTPUT := $(O)
endif
# That’s our default target when none is given on the command line
# 当命令行中没有给出目标时,这作为默认目标。
PHONY := _all
_all:
# Cancel implicit rules on top Makefile
# 取消顶层Makefile隐含规则
$(CURDIR)/Makefile Makefile: ; //没有查清楚此语句如何取消隐含规则
ifneq ($(KBUILD_OUTPUT),)
# Invoke a second make in the output directory, passing relevant variables
# check that the output directory actually exists
PS:这个地方翻译不太理解,大致意思是检测KBUILD_OUTPUT目录是否存在。
saved-output := $(KBUILD_OUTPUT)
KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd) //KBUILD_OUTPUT变为绝对路径
$(if $(KBUILD_OUTPUT),, \
$(error output directory “$(saved-output)” does not exist))
//如果KBUILD_OUTPUT为空,打印错误输出*** output directory “xxx” does not exist。
PHONY += $(MAKECMDGOALS) sub-make //MAKECMDGOALS指定make的终极目标
$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make
$(Q)@: //过滤掉$(MAKECMDGOALS)中_all sub-make
//$(CURDIR)/Makefile
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))
//(if $(KBUILD_VERBOSE:1=),@)使用了变量的替换引用。格式为“ $(VAR:A=B)”(或者“ ${VAR:A=B}”),意思是,
//替换变量“ VAR”中所有“ A”字符结尾的字为“ B”结尾的字。KBUILD_VERBOSE结尾为1时,整个表达式为空,否则
//为@,为@时是希望后面的命令能够静默执行。
# Leave processing to above invocation of make
# 离开处理到上面的make调用
skip-makefile := 1
endif # ifneq ($(KBUILD_OUTPUT),)
endif # ifeq ($(KBUILD_SRC),)
# We process the rest of the Makefile if this is the final invocation of make
# 如果这是最后的make调用,我们处理Makefile剩余部分
ifeq ($(skip-makefile),)
# If building an external module we do not care about the all: rule
# but instead _all depend on modules
# 如果是构建一个外部模块,我们不用关心all的规则,但是需要关系_all所以来的modules
PHONY += all
ifeq ($(KBUILD_EXTMOD),)
_all: all
else
_all: modules
endif
srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))
objtree := $(CURDIR)
src := $(srctree)
obj := $(objtree)
VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
export srctree objtree VPATH
# 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通知自定义模式构建什么样的底层架构。这是最先设置的地方,如果自定义模式构建开始了,”ARCH=um”
# 命令行参数重写ARCH设置。如果本地构建开始了,ARCH被指定好了,不管获取到什么值,SUBARCH随后都会被忽
# 略。
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/ )
# 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.
# 交叉编译,选择不同的gcc/bin-utils设置。
# —————————————————————————
#
# 当执行别的架构下交叉编译,ARCH被设置成目标架构.(看arch/*可以知道支持哪些架构)。
# ARCH在make调用的过程中可以设置:
# make ARCH=ia64
# 另外一种方法是在环境变量中设置ARCH。
# 默认的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和bin-utils可执行文件才会有
# $(CROSS_COMPILE)前缀。
# CROSS_COMPILE可以在命令行中设置。
# make CROSS_COMPILE=ia64-linux-
# CROSS_COMPILE也可以在环境变量中设置。
# CROSS_COMPILE默认值是没有前缀的。
# 注意:一些架构在它们arch/*/Makefile文件中指定CROSS_COMPILE变量。
export KBUILD_BUILDHOST := $(SUBARCH)
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-
# Architecture as present in compile.h
UTS_MACHINE := $(ARCH)
SRCARCH := $(ARCH)
# Additional ARCH settings for x86
# 对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
# SHELL used by kbuild
# kbuild使用的SHELL
CONFIG_SHELL := $(shell if [ -x “$$BASH” ]; then echo $$BASH; \ //如果$BASH存在且是可执行的则为真
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi ; fi)
HOSTCC = gcc
HOSTCXX = g++
HOSTCFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer
HOSTCXXFLAGS = -O2
//-Wall 基本打开了所有需要注意的警告信息。
//-Wmissing-prototypes
//-Wstrict-prototypes 使用了非原型的函数声明时给出警告
//-O2 编译优化级别
//-fomit-frame-pointer
# Decide whether to build built-in, modular, or both.
# Normally, just do built-in.
# 决定构建built-in还是modular,或者两者同时构建。
# 通常之构建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.
# 如果我们执行”make modules”,就不编译built-in目标。
# 当我们使用modversions构建模块,我们需要在进入下层目录是考虑built-in目标,这是为了确保在我们记录
# 他们之间,校验码是最新的。PS:这点不理解,翻译的也有问题。
ifeq ($(MAKECMDGOALS),modules)
KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1)
endif
# 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
# 如果我们执行了”make <whatever> modules”,就会编译我们指定的模块。
# 只执行”make”或”make all”将会编译所有模块。
ifneq ($(filter all _all modules,$(MAKECMDGOALS)),)
KBUILD_MODULES := 1
endif
ifeq ($(MAKECMDGOALS),)
KBUILD_MODULES := 1
endif
export KBUILD_MODULES KBUILD_BUILTIN
export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD
# 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.
# 美化输出
# —————————————————————————
#
# 通常我们在执行命令前会输出整个命令。通过echo $($(quiet)$(cmd)),我们现在可以设置$(quiet)去选择别的
# 输出格式,例如:
#
# quiet_cmd_cc_o_c = Compiling $(RELDIR)/$@
# cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
#
# 如果$(quiet)为空,整个命令都会被打印。
# 如果$(quiet)被设置成”quiet_”,只有会打印简短的版本。
# 如果$(quiet)被设置成”silent_”,什么都不会打印,因为$(silent_cmd_cc_o_c)值不存在。
#
# 一个简单的变量$(Q)被用于命令行的前缀——这对于在非冗余模式下隐藏命令非常有用。
#
# $(Q)ln $@ :<
#
# 如果KBUILD_VERBOSE等于0,上面的命令就会被隐藏。
# 如果KBUILD_VERBOSE等于1,上面的命令就会被打印。
ifeq ($(KBUILD_VERBOSE),1)
quiet =
Q =
else
quiet=quiet_
Q = @
endif
# If the user is running make -s (silent mode), suppress echoing of
# commands
# 如果用户运行make -s(静默模式),限制所有命令的输出。
ifneq ($(findstring s,$(MAKEFLAGS)),) //在$(MAKEFLAGS)中找到字符串s
quiet=silent_
endif
export quiet Q KBUILD_VERBOSE
# Look for make include files relative to root of kernel src
# 设置包含文件的路径,该路径相对于内核源码的根目录。
MAKEFLAGS += –include-dir=$(srctree) //用于make递归调用时传递命令行选项
# We need some generic definitions (do not try to remake the file).
# 我们需要一些一般的定义(不要尝试remake这个文件)。
$(srctree)/scripts/Kbuild.include: ;
include $(srctree)/scripts/Kbuild.include
# Make variables (CC, etc…)
# Make变量(CC,等…)
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
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 = -T $(srctree)/scripts/module-common.lds
CFLAGS_KERNEL =
AFLAGS_KERNEL =
CFLAGS_GCOV = -fprofile-arcs -ftest-coverage