【学习笔记】【构建】【Makefile】u-boot 顶层 Makefile 详细注释

本文是基于 u-boot 的顶层 Makefile 做注释分析,试图了解大型项目的构建编译过程。这里是分析到 Makefile 的基本的初始化工作完成,后面就是对目标文件的编译和构建过程,针对每套代码编译和构建过程是有差异的后面遇到的具体的代码,如果需要再做具体分析。

1、makefile 规则

1.1 makefile 规则格式

target ... : prerequisites ...
command

target : 是一个目标文件,可以是任意的实际文件,也可以是一个标签;
prerequisites: 是生成目标文件所依赖的文件或目标;
command: 生成目标的命令或者规则,依赖文件通过一定的规则生成目标文件;

这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。

在 makefile 中,第一个目标被认为是默认目标。

1.2 运算符

1.2.1 赋值运算符
 =  :最基本的赋值
 += :给变量追加值
 := : 覆盖之前的值
 ?= : 如果没有被赋值过,就给赋予当前的值

1.3 变量

makefile 中默认变量默认变量的作用
$@表示目标文件名称,包含扩展名
$^表示所有的依赖文件,以空格隔开,不重复
$<表示第一个依赖文件的名称
$+表示所有的依赖文件,空格隔开,可以重复
$*表示目标文件的名称,不包含扩展名
$?依赖项中,所有比目标文件新的依赖文件

1.4 语法

1.4.1 条件表达式
// 条件表达式的语法为:
<conditional-directive>
<text-if-true>
endif
// 以及:
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif

其中 conditional-directive 表示条件关键字
第一个条件关键字 ifeq

// 比较参数 arg1 和 arg2 的值是否相同
// 相同执行 <text-if-true>
// 否则执行 <text-if-false>
ifeq (<arg1>, <arg2>)
ifeq '<arg1>' '<arg2>'
ifeq "<arg1>" "<arg2>"
ifeq "<arg1>" '<arg2>'
ifeq '<arg1>' "<arg2>"

第二个关键字 ifneq

// 比较参数 arg1 和 arg2 的值是否不相同
// 不相同执行 <text-if-true>
// 否则执行 <text-if-false>
ifneq (<arg1>, <arg2>)
ifneq '<arg1>' '<arg2>'
ifneq "<arg1>" "<arg2>"
ifneq "<arg1>" '<arg2>'
ifneq '<arg1>' "<arg2>"

第三个关键字 ifdef

// 判断 variable-name 是否非空,如果非空则为真,如果是空则为假
ifdef <variable-name>

第四个关键字 ifndef

// 判断 variable-name 是否为空,如果空则为真,如果非空则为假
ifndef <variable-name>

1.5 通配符

通配符使用说明
*匹配 0个或者多个任意字符
?匹配任意一个字符
[]我们可以指定匹配的字符放在[] 中
%匹配 0 个或者多个任意字符

2、makefile 中的所有目标

将 PHONY 的值打印出来,我们可以发现,makefile中的所有目标都在这个里面,后面将从imx6ul 编译过程,对目标是怎么来的,依赖什么,规则是什么这些方面对整个工程的 makefile 做出介绍。

_all all scripts_basic outputmakefile dtbs arch/arm/cpu arch/arm/cpu/armv7 arch/arm/imx-common arch/arm/lib 
board/freescale/common board/freescale/mx6ullevk cmd common disk drivers drivers/dma drivers/gpio drivers/i2c 
drivers/mmc drivers/mtd drivers/mtd/onenand drivers/mtd/spi drivers/net drivers/net/phy drivers/pci drivers/power 
drivers/power/battery drivers/power/fuel_gauge drivers/power/mfd drivers/power/pmic drivers/power/regulator 
drivers/serial drivers/spi drivers/usb/dwc3 drivers/usb/emul drivers/usb/eth drivers/usb/gadget 
drivers/usb/gadget/udc drivers/usb/host drivers/usb/musb-new drivers/usb/musb drivers/usb/phy drivers/usb/ulpi fs lib 
net test test/dm tools examples prepare archprepare prepare0 prepare1 prepare2 prepare3 _clean_api 
_clean_arch/arm/cpu _clean_arch/arm/cpu/armv7 _clean_arch/arm/imx-common _clean_arch/arm/lib 
_clean_board/freescale/common _clean_board/freescale/mx6ullevk _clean_cmd _clean_common _clean_disk _clean_drivers 
_clean_drivers/ddr/altera _clean_drivers/ddr/fsl _clean_drivers/dma _clean_drivers/gpio _clean_drivers/i2c 
_clean_drivers/mmc _clean_drivers/mtd _clean_drivers/mtd/nand _clean_drivers/mtd/onenand _clean_drivers/mtd/spi 
_clean_drivers/mtd/ubi _clean_drivers/net _clean_drivers/net/fm _clean_drivers/net/phy _clean_drivers/pci 
_clean_drivers/power _clean_drivers/power/battery _clean_drivers/power/fuel_gauge _clean_drivers/power/mfd 
_clean_drivers/power/pmic _clean_drivers/power/regulator _clean_drivers/serial _clean_drivers/spi 
_clean_drivers/usb/dwc3 _clean_drivers/usb/emul _clean_drivers/usb/eth _clean_drivers/usb/gadget 
_clean_drivers/usb/gadget/udc _clean_drivers/usb/host _clean_drivers/usb/musb _clean_drivers/usb/musb-new 
_clean_drivers/usb/phy _clean_drivers/usb/ulpi _clean_dts _clean_examples _clean_fs _clean_lib _clean_net _clean_post 
_clean_test _clean_test/dm _clean_test/env _clean_tools _clean_doc/DocBook clean archclean _mrproper_scripts mrproper 
archmrproper distclean prepare scripts checkstack ubootrelease ubootversion FORCE

2.1 _all

// PHONY 是一个全局变量,里面保存着 Makefile 中的所有目标
PHONY := _all
// 只给出了一个目标,没有给出依赖和规则,就相当于一个声明,一般为了将目标放到最前面作为默认目标,暂时不给依赖和规则
// 后面可以重新给出依赖和规则
_all:

PHONY += all
ifeq ($(KBUILD_EXTMOD),)
_all: all
else
_all: modules
endif

目标:
	_all
依赖:
	all
规则:
	无

目标:
	_all
依赖:
	modules
规则:
	无
注:
1、目标后面可以有依赖,也可以没有依赖;
2、目标后面可以有规则,也可以没有规则;

2.2 all

all:		$(ALL-y)
ifneq ($(CONFIG_SYS_GENERIC_BOARD),y)
	@echo "===================== WARNING ======================"
	@echo "Please convert this board to generic board."
	@echo "Otherwise it will be removed by the end of 2014."
	@echo "See doc/README.generic-board for further information"
	@echo "===================================================="
endif
ifeq ($(CONFIG_DM_I2C_COMPAT),y)
	@echo "===================== WARNING ======================"
	@echo "This board uses CONFIG_DM_I2C_COMPAT. Please remove"
	@echo "(possibly in a subsequent patch in your series)"
	@echo "before sending patches to the mailing list."
	@echo "===================================================="
endif

目标:
	all
依赖:
	ALL-y
规则:
	如果 CONFIG_SYS_GENERIC_BOARD 不等于 y,则打印
		@echo "===================== WARNING ======================"
		@echo "Please convert this board to generic board."
		@echo "Otherwise it will be removed by the end of 2014."
		@echo "See doc/README.generic-board for further information"
		@echo "===================================================="
	如果 CONFIG_DM_I2C_COMPAT 等于 y,则打印
		@echo "===================== WARNING ======================"
		@echo "This board uses CONFIG_DM_I2C_COMPAT. Please remove"
		@echo "(possibly in a subsequent patch in your series)"
		@echo "before sending patches to the mailing list."
		@echo "===================================================="
注:
1、目标后面的规则可是任意的,可以和规则依赖有关系,也可以和规则依赖没有关系;
2、依赖可以是已经生成的目标,也可以是其他的文件;

2.3 ALL-y

# Always append ALL so that arch config.mk's can add custom ones
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check

ALL-$(CONFIG_ONENAND_U_BOOT) += u-boot-onenand.bin
ifeq ($(CONFIG_SPL_FSL_PBL),y)
ALL-$(CONFIG_RAMBOOT_PBL) += u-boot-with-spl-pbl.bin
else
ifneq ($(CONFIG_SECURE_BOOT), y)
# For Secure Boot The Image needs to be signed and Header must also
# be included. So The image has to be built explicitly
ALL-$(CONFIG_RAMBOOT_PBL) += u-boot.pbl
endif
endif
ALL-$(CONFIG_SPL) += spl/u-boot-spl.bin
ALL-$(CONFIG_SPL_FRAMEWORK) += u-boot.img
ALL-$(CONFIG_TPL) += tpl/u-boot-tpl.bin
ALL-$(CONFIG_OF_SEPARATE) += u-boot.dtb
ifeq ($(CONFIG_SPL_FRAMEWORK),y)
ALL-$(CONFIG_OF_SEPARATE) += u-boot-dtb.img
endif
ALL-$(CONFIG_OF_HOSTFILE) += u-boot.dtb
ifneq ($(CONFIG_SPL_TARGET),)
ALL-$(CONFIG_SPL) += $(CONFIG_SPL_TARGET:"%"=%)
endif
ALL-$(CONFIG_REMAKE_ELF) += u-boot.elf
ALL-$(CONFIG_EFI_APP) += u-boot-app.efi
ALL-$(CONFIG_EFI_STUB) += u-boot-payload.efi

ifneq ($(BUILD_ROM),)
ALL-$(CONFIG_X86_RESET_VECTOR) += u-boot.rom
endif

# enable combined SPL/u-boot/dtb rules for tegra
ifeq ($(CONFIG_TEGRA)$(CONFIG_SPL),yy)
ALL-y += u-boot-tegra.bin u-boot-nodtb-tegra.bin
ALL-$(CONFIG_OF_SEPARATE) += u-boot-dtb-tegra.bin
endif

# Add optional build target if defined in board/cpu/soc headers
ifneq ($(CONFIG_BUILD_TARGET),)
ALL-y += $(CONFIG_BUILD_TARGET:"%"=%)
endif

ALL-y 不是目标,是一系列的文件
ALL-y = checkarmreloc u-boot.imx u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check
checkarmreloc u-boot.imx 这两个文件暂时没有找到出处

2.4 u-boot.bin

ifeq ($(CONFIG_OF_SEPARATE),y)
u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
	$(call if_changed,cat)

u-boot.bin: u-boot-dtb.bin FORCE
	$(call if_changed,copy)
else
u-boot.bin: u-boot-nodtb.bin FORCE
	$(call if_changed,copy)
endif

目标:
	u-boot.bin
依赖:
	u-boot-nodtb.bin FORCE
规则:
	$(call if_changed,copy)
注:
1、这里规则的具体形式后面再分析;

2.5 FORCE

PHONY += FORCE
FORCE:

FORCE 既没有依赖的规则,其底下也没有可执行的命令
如果一个规则没有命令或者依赖,而且它的目标不是一个存在的文件名,在执行此规则时,目标总会被认为是最新的。也就是说,
这个规则一旦被执行,make 就认为它所表示的目标已经被更新过。当将这样的目标(FORCE)作为一个规则的依赖时,由于依赖总
被认为是被更新过的,所以作为依赖所在的规则定义的命令总会被执行;

2.6 u-boot-nodtb.bin

u-boot-nodtb.bin: u-boot FORCE
	$(call if_changed,objcopy)
	$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
	$(BOARD_SIZE_CHECK)

目标:
	u-boot-nodtb.bin
依赖:
	u-boot FORCE
规则:
	$(call if_changed,objcopy)
	$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
	$(BOARD_SIZE_CHECK)
注:
1、这里的规则也放到后面分析;

2.7 u-boot

u-boot:	$(u-boot-init) $(u-boot-main) u-boot.lds FORCE
	$(call if_changed,u-boot__)
ifeq ($(CONFIG_KALLSYMS),y)
	$(call cmd,smap)
	$(call cmd,u-boot__) common/system_map.o
endif

目标:
	u-boot
依赖:
	$(u-boot-init) $(u-boot-main) u-boot.lds FORCE
规则:
	$(call if_changed,u-boot__)
注:
1、CONFIG_KALLSYMS 没有值,所以不会走到下面;
2、这里的规则也放到后面分析;

arm-linux-gnueabihf-ld.bfd -pie  --gc-sections -Bstatic -Ttext 0x87800000 -o u-boot -T u-boot.lds
arch/arm/cpu/armv7/start.o --start-group  arch/arm/cpu/built-in.o  arch/arm/cpu/armv7/built-in.o
arch/arm/imx-common/built-in.o  arch/arm/lib/built-in.o  board/freescale/common/built-in.o
board/freescale/mx6ullevk/built-in.o cmd/built-in.o  common/built-in.o  disk/built-in.o  drivers/built-in.o
drivers/dma/built-in.o  drivers/gpio/built-in.o  drivers/i2c/built-in.o  drivers/mmc/built-in.o
drivers/mtd/built-in.o  drivers/mtd/onenand/built-in.o drivers/mtd/spi/built-in.o  drivers/net/built-in.o
drivers/net/phy/built-in.o  drivers/pci/built-in.o drivers/power/built-in.o  drivers/power/battery/built-in.o
drivers/power/fuel_gauge/built-in.o drivers/power/mfd/built-in.o  drivers/power/pmic/built-in.o
drivers/power/regulator/built-in.o drivers/serial/built-in.o  drivers/spi/built-in.o drivers/usb/dwc3/built-in.o
drivers/usb/emul/built-in.o drivers/usb/eth/built-in.o  drivers/usb/gadget/built-in.o
drivers/usb/gadget/udc/built-in.o  drivers/usb/host/built-in.o  drivers/usb/musb-new/built-in.o
drivers/usb/musb/built-in.o  drivers/usb/phy/built-in.o drivers/usb/ulpi/built-in.o  fs/built-in.o
lib/built-in.o  net/built-in.o  test/built-in.o  test/dm/built-in.o --end-group arch/arm/lib/eabi_compat.o
-L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/4.9.4 
-lgcc -Map u-boot.map

2.8 u-boot-init

u-boot-init := $(head-y)

u-boot-init 不是目标,是一系列文件的组合;

2.9 u-boot-main

u-boot-main := $(libs-y)

u-boot-main 不是目标,是一系列文件的组合;

2.10 u-boot.lds

u-boot.lds: $(LDSCRIPT) prepare FORCE
	$(call if_changed_dep,cpp_lds)

目标:
	u-boot.lds
依赖:
	$(LDSCRIPT) prepare FORCE
规则:
	$(call if_changed_dep,cpp_lds)
注:
1、这里的规则也放到后面分析;

2.11 head-y

head-y 是一个全局变量,在多个 makefile 中有赋值;
现在的疑问是,他是怎么找到我们需要的那个 makefile 的

2.12 config

// 暂不分析

2.13 %config

/******************************************************************************
%config: scripts_basic outputmakefile FORCE
	$(Q)$(MAKE) $(build)=scripts/kconfig $@

目标:%config  --- mx6ull_14x14_ddr512_emmc_defconfig
依赖:scripts_basic outputmakefile FORCE
规则:$(Q)$(MAKE) $(build)=scripts/kconfig $@
     Q = @ 打印这一句
 *****************************************************************************/

2.14 scripts_basic

/****************************************************************************
scripts_basic:
	$(Q)$(MAKE) $(build)=scripts/basic
	$(Q)rm -f .tmp_quiet_recordmcount

目标:scripts_basic
依赖:无
规则:
	$(Q)$(MAKE) $(build)=scripts/basic
	$(Q)rm -f .tmp_quiet_recordmcount
 ***************************************************************************/

3、make 中的函数

3.1 filter

/****************************************************************************
函数名:过滤函数
函数使用格式:$(filter <pattern...>,<text>)
功能:以 <pattern...> 模式过滤 <text> 字符串中的单词,保留符合模式 <pattern...>
     的单词,可以有多个模式
返回值: 返回符合模式 <pattern...> 的字符串
 ***************************************************************************/

3.2 words

/****************************************************************************
函数名:单词个数统计
函数使用格式:$(words <text>)
功能:统计 <text> 中字符串的单词个数
返回值: 返回 <text> 中单词的个数
***************************************************************************/

4、makefile 解析

#
# SPDX-License-Identifier:	GPL-2.0+
#
# 主版本号
VERSION = 2016
# 补丁版本号
PATCHLEVEL = 03
# 次版本号
SUBLEVEL =
# 附加版本信息
EXTRAVERSION =
# 和名字相关
NAME =

# =  :最基本的赋值
# += :给变量追加值
# := : 覆盖之前的值
# ?= : 如果没有被赋值过,就给赋予当前的值
# *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.

# o Do not use make's built-in rules and variables
#   (this increases performance and avoids hard-to-debug behaviour);
# o Look for make include files relative to root of kernel src
# CURDIR :Makefile 的内置变量,是当前 Makefile 的绝对路径,相当于 shell 命令的 "pwd"
# --include-dir : 如果 Makefile 在执行时,有 -I 或者 --include-dir 指定的路径,那么 make 就会在这两个参数指定的路径下去找
# -r :禁止 make 使用任何隐含规则
# -R :禁止 make 使用任何作用于变量上的隐含规则
# MAKEFLAGS 系统级的变量,只要定义了就会传递到底层 Makefile 中
# MAKEFLAGS = rR -I/home/star/work/nxp/uboot-imx-rel_imx_4.1.15_2.1.0_ga --no-print-directory
MAKEFLAGS += -rR --include-dir=$(CURDIR)

# Avoid funny character set dependencies
# unexport 修饰的变量 LC_ALL 只能在当前 Makefile 文件中使用
# LC_ALL 这里只是定义了下没有给初始值,并且声明只能在本文件中使用
# LC_COLLATE 这里定义了并且给了初始值,传递给下层 Makefile
# LC_NUMERIC 这里定义了并且给了初始值,传递给下层 Makefile
unexport LC_ALL
LC_COLLATE=C
LC_NUMERIC=C
export LC_COLLATE LC_NUMERIC

# Avoid interference with shell env settings
# GREP_OPTIONS 这里只是定义了下没有给初始值,并且声明只能在本文件中使用,但是本文件中没有看到使用的地方
unexport GREP_OPTIONS

# 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.

# 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.
#
# To put more focus on warnings, be less verbose as default
# Use 'make V=1' to see the full commands
# ifeq($(one), $(two)) 条件表达式,如果 one 和 two 的值相同则条件成立
# origin <variable> 判断变量 variable 的来历,并返回对应的标识
# 返回 undefined 表示变量从来没有定义过
# 返回 default 表示变量是一个默认变量和 environment有关
# 返回 file 表示变量被定义在 Makefile 中
# 返回 command line 表示变量被命令行定义
# 返回 override 表示变量是被 override 指示符重新定义的
# 返回 automatic 表示变量是一个命令运行中的自动化变量
# 这一句的含义就是判断变量 V 是否在命令行中定义 
ifeq ("$(origin V)", "command line")
  # 如果是则 KBUILD_VERBOSE = $(V)
  KBUILD_VERBOSE = $(V)
endif
# 这里的 KBUILD_VERBOSE 和 V 涉及到构建时候日志打印
# 0 :构建日志简易打印
# 1:构建日志详细打印
# ifndef <variable-name> 判断变量 variable-name 是否为空,如果为空则条件成立
# 这一句的含义就是如果 KBUILD_VERBOSE 没有被定义过,值为空则 KBUILD_VERBOSE = 0
ifndef KBUILD_VERBOSE
  KBUILD_VERBOSE = 0
endif

# 如果 KBUILD_VERBOSE = 1 则 quiet 是空, Q 也是空
# 否则 quiet=quiet_, Q = @
# 如果 $(quiet) 为空,则日志中将打印整个命令, cmd_cc_o_c
# 如果 $(quiet) 设置为 quiet_,则日志中将打印短版本命令, quiet_cmd_cc_o_c
# 如果 $(quiet) 设置为 silent_,则将不会打印日志, silent_cmd_cc_o_c 不存在
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_VERSION Makefile 内置的标准变量 make命令的版本号,也就是执行 make -v 时看到的版本号
# MAKE_VIRSION = 4.1
# Makefile 中的 % 通配符 4.% 表示匹配所有的 4. 字符串
# filter <pattern...>, <text> 过滤函数 表示以 pattern 模式过滤 text 字符串中的单词,保留符合模式 pattern 的单词
# 判断当前 Makefile 的版本是否是 4.x,是则条件成立
ifneq ($(filter 4.%,$(MAKE_VERSION)),)	# make-4
# MAKEFLAGS = rR -I/home/star/work/nxp/uboot-imx-rel_imx_4.1.15_2.1.0_ga --no-print-directory
# firstword <text> 返回首单词函数, 返回字符串 text 中的第一个单词
# $(firstword x$(MAKEFLAGS)) = xrR
# 判断 MAKEFLAGS 第一个字符串是不是以字母 s 结尾, 这里不包含,所以条件不成立
ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
  quiet=silent_
endif
else					# make-3.8x
# 判断 MAKEFLAGS 中有没有包含 s 或 -s 开头的字符串,这里没有包含所以条件不成立
ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
  quiet=silent_
endif
endif

export quiet Q KBUILD_VERBOSE

# 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_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 没有定义所以是空的,ifeq 条件成立
ifeq ($(KBUILD_SRC),)

# OK, Make called in directory where kernel src resides
# Do we want to locate output files in a separate directory?
# 如果在命令行定义了 O, 则条件成立 KBUILD_OUTPUT := $(O)
ifeq ("$(origin O)", "command line")
  KBUILD_OUTPUT := $(O)
endif

# That's our default target when none is given on the command line
# 给 PHONY 赋值,并且覆盖之前的值,在这里 PHONY 只等于 _all
PHONY := _all
# _all Makefile 文件的第一个目标也是 Makefile 的终极目标,暂时没有看到依赖和指令
_all:

# Cancel implicit rules on top Makefile
# 取消执行路径下 Makefile 的隐含规则
$(CURDIR)/Makefile Makefile: ;

# 如果在命令行没有定义 O, 那么 KBUILD_OUTPUT 就是空的
# 目前没有遇到过在执行 make 时定义 O 的场景, 所以这里面没有进去过,先不看
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),)

# We process the rest of the Makefile if this is the final invocation of make
# 没有遇到过执行 make 时定义 O 的场景, 所以 skip-makefile 一直都是空值
# 条件成立
ifeq ($(skip-makefile),)

# Do not print "Entering directory ...",
# but we want to display it when entering to the output directory
# so that IDEs/editors are able to understand relative filenames.
# --no-print-directory 在一个Makefile中调用另一个Makefile这个场景下,编译日志中禁止打印类似"离开某一个目录"、"进入某另一个目录" 这样的打印
MAKEFLAGS += --no-print-directory

# 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 是来自命令行定义的,则条件成立
# 单编模块时候 C 会用到,所以这里暂时也不看
ifeq ("$(origin C)", "command line")
  KBUILD_CHECKSRC = $(C)
endif
# 一般全编译这里 KBUILD_CHECKSRC 是没有值的,条件成立
# 所以通常情况下 KBUILD_CHECKSRC = 0
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
# 一般情况下 SUBDIRS 是空的,条件不成立
ifdef SUBDIRS
  KBUILD_EXTMOD ?= $(SUBDIRS)
endif

# 如果 M 定义来自命令行则条件成立
# KBUILD_EXTMOD := $(M)
# M 在通常条件下也不会使用命令行定义
ifeq ("$(origin M)", "command line")
  KBUILD_EXTMOD := $(M)
endif

# If building an external module we do not care about the all: rule
# but instead _all depend on modules
# PHONY = _all all scripts_basic outputmakefile dtbs arch/arm/cpu arch/arm/cpu/armv7 arch/arm/imx-common \
# arch/arm/lib board/freescale/common board/freescale/mx6ullevk cmd common disk drivers drivers/dma \
# drivers/gpio drivers/i2c drivers/mmc drivers/mtd drivers/mtd/onenand drivers/mtd/spi drivers/net \
# drivers/net/phy drivers/pci drivers/power drivers/power/battery drivers/power/fuel_gauge drivers/power/mfd \
# drivers/power/pmic drivers/power/regulator drivers/serial drivers/spi drivers/usb/dwc3 drivers/usb/emul \
# drivers/usb/eth drivers/usb/gadget drivers/usb/gadget/udc drivers/usb/host drivers/usb/musb-new drivers/usb/musb \
# drivers/usb/phy drivers/usb/ulpi fs lib net test test/dm tools examples prepare archprepare prepare0 prepare1 \
# prepare2 prepare3 _clean_api _clean_arch/arm/cpu _clean_arch/arm/cpu/armv7 _clean_arch/arm/imx-common _clean_arch/arm/lib \
# _clean_board/freescale/common _clean_board/freescale/mx6ullevk _clean_cmd _clean_common _clean_disk _clean_drivers \
# _clean_drivers/ddr/altera _clean_drivers/ddr/fsl _clean_drivers/dma _clean_drivers/gpio _clean_drivers/i2c _clean_drivers/mmc \
# _clean_drivers/mtd _clean_drivers/mtd/nand _clean_drivers/mtd/onenand _clean_drivers/mtd/spi _clean_drivers/mtd/ubi _clean_drivers/net \
# _clean_drivers/net/fm _clean_drivers/net/phy _clean_drivers/pci _clean_drivers/power _clean_drivers/power/battery \
# _clean_drivers/power/fuel_gauge _clean_drivers/power/mfd _clean_drivers/power/pmic _clean_drivers/power/regulator _clean_drivers/serial \
# _clean_drivers/spi _clean_drivers/usb/dwc3 _clean_drivers/usb/emul _clean_drivers/usb/eth _clean_drivers/usb/gadget _clean_drivers/usb/gadget/udc \
# _clean_drivers/usb/host _clean_drivers/usb/musb _clean_drivers/usb/musb-new _clean_drivers/usb/phy _clean_drivers/usb/ulpi _clean_dts _clean_examples \
# _clean_fs _clean_lib _clean_net _clean_post _clean_test _clean_test/dm _clean_test/env _clean_tools _clean_doc/DocBook clean archclean \
# _mrproper_scripts mrproper archmrproper distclean prepare scripts checkstack ubootrelease ubootversion FORCE
# PHONY 的值比较长,但是暂时没有看出有什么含义
PHONY += all
# KBUILD_EXTMOD 的值在命令行定义了 M 的条件下才会有值,所以条件成立
ifeq ($(KBUILD_EXTMOD),)
# _all 依赖 all
_all: all
else
_all: modules
endif
# 这里的 KBUILD_SRC 还是空的,条件成立
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

# srctree := .
objtree		:= .
src		:= $(srctree)
obj		:= $(objtree)

# if <condition>,<then-part>,<else-part> 如果 condition 为真则执行 then-part部分,否则执行 else-part部分
# 这里 KBUILD_EXTMOD 也是空值
# VPATH := .
VPATH		:= $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))

export srctree objtree VPATH

# Make sure CDPATH settings don't interfere
# CDPATH 变量在这里定义了,但是没有使用
unexport CDPATH

#########################################################################
# shell 命令 uname -m 获取的是本地机器的类型 x86_64
# sed -e s/text1/text2/ 是把 text1的值替换为 text2 的值
# 所以这里 HOSTARCH := x86_64
HOSTARCH := $(shell uname -m | \
	sed -e s/i.86/x86/ \
	    -e s/sun4u/sparc64/ \
	    -e s/arm.*/arm/ \
	    -e s/sa110/arm/ \
	    -e s/ppc64/powerpc/ \
	    -e s/ppc/powerpc/ \
	    -e s/macppc/powerpc/\
	    -e s/sh.*/sh/)

# shell 命令 uname -s 获取的是本地机器的类型 Linux
# tr '[:upper:]' '[:lower:]' 是把大写字母换成小写字母
# 所以这里 HOSTOS := linux
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
	    sed -e 's/\(cygwin\).*/cygwin/')

export	HOSTARCH HOSTOS

#########################################################################

# set default to nothing for native builds
# 如果本地环境 HOSTARCH 和我将要编译的镜像的环境 ARCH 是一样的
# CROSS_COMPILE ?= 是空,也就是 gcc
# 但是正点原子这里是不一样的
# 正点原子使用的是 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
# 这里为了后面编译方便,我们也给强制赋值
ARCH ?= arm 
CROSS_COMPILE ?= arm-linux-gnueabihf-
# 这里条件不成立
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif

KCONFIG_CONFIG	?= .config
export KCONFIG_CONFIG

# SHELL used by kbuild
# 这里是获取本地环境中 shell 脚本的运行环境
# CONFIG_SHELL = /bin/bash
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
	  else if [ -x /bin/bash ]; then echo /bin/bash; \
	  else echo sh; fi ; fi)

HOSTCC       = cc
HOSTCXX      = c++
HOSTCFLAGS   = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
HOSTCXXFLAGS = -O2

# HOSTOS := linux 条件不成立
ifeq ($(HOSTOS),cygwin)
HOSTCFLAGS	+= -ansi
endif

# Mac OS X / Darwin's C preprocessor is Apple specific.  It
# generates numerous errors and warnings.  We want to bypass it
# and use GNU C's cpp.	To do this we pass the -traditional-cpp
# option to the compiler.  Note that the -traditional-cpp flag
# DOES NOT have the same semantics as GNU C's flag, all it does
# is invoke the GNU preprocessor in stock ANSI/ISO C fashion.
#
# Apple's linker is similar, thanks to the new 2 stage linking
# multiple symbol definitions are treated as errors, hence the
# -multiply_defined suppress option to turn off this error.
#
# HOSTOS := linux 条件不成立,这个分支不会进去,暂时不看
ifeq ($(HOSTOS),darwin)
# get major and minor product version (e.g. '10' and '6' for Snow Leopard)
DARWIN_MAJOR_VERSION	= $(shell sw_vers -productVersion | cut -f 1 -d '.')
DARWIN_MINOR_VERSION	= $(shell sw_vers -productVersion | cut -f 2 -d '.')

os_x_before	= $(shell if [ $(DARWIN_MAJOR_VERSION) -le $(1) -a \
	$(DARWIN_MINOR_VERSION) -le $(2) ] ; then echo "$(3)"; else echo "$(4)"; fi ;)

# Snow Leopards build environment has no longer restrictions as described above
HOSTCC       = $(call os_x_before, 10, 5, "cc", "gcc")
HOSTCFLAGS  += $(call os_x_before, 10, 4, "-traditional-cpp")
HOSTLDFLAGS += $(call os_x_before, 10, 5, "-multiply_defined suppress")

# since Lion (10.7) ASLR is on by default, but we use linker generated lists
# in some host tools which is a problem then ... so disable ASLR for these
# tools
HOSTLDFLAGS += $(call os_x_before, 10, 7, "", "-Xlinker -no_pie")
endif

# 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.

# MAKECMDGOALS 变量是 Makefile 的内置变量,指定终极目标,如果在命令行没有指定
# 那么这个变量就是空值
# 全编译一般这个变量是空值
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

# U-Boot does not need modules
#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

# We need some generic definitions (do not try to remake the file).
# 这一句的含义是禁止 scripts/Kbuild.include 这个文件的隐含规则
scripts/Kbuild.include: ;
# include 是进入这个文件中
include scripts/Kbuild.include

# Make variables (CC, etc...)

AS		= $(CROSS_COMPILE)as
# Always use GNU ld
# /dev/null 是一个字符设备,就是输出到这个设备后,信息就没有了
# Unix 或 Linux 中:
#         0 代表标准输入流, 也就是:stdin
#         1代表标准输出流, 也就是:stdout
#         2代表标准错误流, 也就是:stderr
# 2> /dev/null 将 标准错误流重定向输出到 /dev/null,就是忽略错误信息
ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),)
LD		= $(CROSS_COMPILE)ld.bfd
else
LD		= $(CROSS_COMPILE)ld
endif
CC		= $(CROSS_COMPILE)gcc
CPP		= $(CC) -E
AR		= $(CROSS_COMPILE)ar
NM		= $(CROSS_COMPILE)nm
LDR		= $(CROSS_COMPILE)ldr
STRIP		= $(CROSS_COMPILE)strip
OBJCOPY		= $(CROSS_COMPILE)objcopy
OBJDUMP		= $(CROSS_COMPILE)objdump
AWK		= awk
PERL		= perl
PYTHON		= python
DTC		= dtc
CHECK		= sparse

# -D Makefile 的宏定义标志符
# -D TRUE = true 等价于 -DTRUE = true 等价于 #define TRUE true
# -D linux 等价于 -Dlinux 等价于 #define linux 1
# 相当于定义变量
CHECKFLAGS     := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
		  -Wbitwise -Wno-return-void -D__CHECK_ENDIAN__ $(CF)

KBUILD_CPPFLAGS := -D__KERNEL__ -D__UBOOT__

# -Wall 开启显示所有警告
# -Wstrict-prototypes 函数声明不是一个原型的警告,要严格按照函数模板
# -Wno-format-security 关闭格式安全警告
# -fno-builtin 当我们定义的函数和内核函数重名时报出警告
# -ffreestanding c编译器的两种实现方式的一种
KBUILD_CFLAGS   := -Wall -Wstrict-prototypes \
		   -Wno-format-security \
		   -fno-builtin -ffreestanding
KBUILD_AFLAGS   := -D__ASSEMBLY__

# Read UBOOTRELEASE from include/config/uboot.release (if it exists)
# UBOOTRELEASE = 2016.3
UBOOTRELEASE = $(shell cat include/config/uboot.release 2> /dev/null)
# VERSION = 2016
# PATCHLEVEL = 03
# SUBLEVEL 在这里是空的
# EXTRAVERSION 在这里也是空的
# UBOOTVERSION = 2016.03
UBOOTVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)

export VERSION PATCHLEVEL SUBLEVEL UBOOTRELEASE UBOOTVERSION
export ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR
export CONFIG_SHELL HOSTCC HOSTCFLAGS HOSTLDFLAGS CROSS_COMPILE AS LD CC
export CPP AR NM LDR STRIP OBJCOPY OBJDUMP
export MAKE AWK PERL PYTHON
export HOSTCXX HOSTCXXFLAGS DTC CHECK CHECKFLAGS

export KBUILD_CPPFLAGS NOSTDINC_FLAGS UBOOTINCLUDE OBJCOPYFLAGS LDFLAGS
export KBUILD_CFLAGS KBUILD_AFLAGS

# 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.
# MODVERDIR := .tmp_versions
export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions

# Files to ignore in find ... statements

# RCS_FIND_IGNORE 和 RCS_TAR_IGNORE 包含了被版本控制系统忽略的文件
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

# ===========================================================================
# Rules shared between *config targets and build targets
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值