目录
版本号
MAKEFLAGS变量
命令输出
静默输出
设置编译结果输出目录
代码检查(一般不需要使用,了解就行)
模块编译(一般不用uboot编译模块,了解就行)
获取主机架构和系统
设置目标架构、交叉编译器和配置文件
包含 scripts/Kbuild.include
交叉编译工具变量设置
导出其他变量
make distclean过程
make xxx_defconfig 过程
分析 make xxx_defconfig
FORCE
outputmakefile
scripts_basic
最后
第一条命令
第二条命令
总结
继续分析一下u-boot.bin生成过程
总结过程
总结
版本号
- VERSION主版本号
- PATCHLEVEL补丁版本号
- SUBLEVEL次版本号
- EXTRAVERSION附加版本信息
- NAME名字
VERSION = 2016 PATCHLEVEL = 03 SUBLEVEL = EXTRAVERSION = NAME =
MAKEFLAGS变量
- 只要不声明为unexport,MAKEFLAGS变量就会自动传到子makefile中
- 大多数是指明当前目录
MAKEFLAGS += -rR --include-dir=$(CURDIR)
- “+=”来给变量 MAKEFLAGS 追加了一些值
- “-rR”表示禁止使用内置的隐 含规则和变量定义
- “--include-dir”指明搜索路径
- ”$(CURDIR)”表示当前目录
命令输出
当编译uboot时,如果加上V=1参数,就会详细打印编译信息,如果不加入只打印简单的编译信息
- 会先判断V这个变量是否是在命令行来的
- 如果是就给quiet和Q变量赋值为空
- 如果不是就给quiet赋值为quiet_ Q赋值为@
- 在后面会用到这个Q变量,@就表示不会在终端输出命令,为空就完整输出命令
- 在后面会用到这个quiet变量,quiet_表示输出短文本,为空表示整个命令都输出,silent_表示整个命令都不会输出
ifeq ("$(origin V)", "command line")KBUILD_VERBOSE = $(V) endif ifndef KBUILD_VERBOSEKBUILD_VERBOSE = 0 endififeq ($(KBUILD_VERBOSE),1)quiet =Q = elsequiet=quiet_Q = @ endif
静默输出
当编译的时候加上-s参数,就会什么都不输出,简单就是quiet=silent_,最后会将三个变量导出去给子makefile
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
endifexport quiet Q KBUILD_VERBOSE
设置编译结果输出目录
make 的时候使用“O”来指定 输出目录,比如“make O=out”就是设置目标文件输出到 out 目录中
也可以不指定 O 参数,不指定的话源文件和编译产生的文件都在 同一个目录内,一般我们不指定 O 参数
ifeq ($(KBUILD_SRC),)
ifeq ("$(origin O)", "command line")KBUILD_OUTPUT := $(O)
endif
PHONY := _all
_all:
$(CURDIR)/Makefile Makefile: ;
ifneq ($(KBUILD_OUTPUT),)
saved-output := $(KBUILD_OUTPUT)
KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT) \ && /bin/pwd)
endif
endif
- 判断“O”是否来自于命令行,如果来自命令行的话条件成立,KBUILD_OUTPUT 就为$(O),因此变量 KBUILD_OUTPUT 就是输出目录
- 判断 KBUILD_OUTPUT 是否为空
- 调用 mkdir 命令,创建 KBUILD_OUTPUT 目录,并且将创建成功以后的绝对路 径赋值给 KBUILD_OUTPUT。至此,通过 O 指定的输出目录就存在了
代码检查(一般不需要使用,了解就行)
使用命令
- “make C=1”使能代码检查,检查那些需要重新编译的文 件。
- “make C=2”用于检查所有的源码文件
ifeq ("$(origin C)", "command line")KBUILD_CHECKSRC = $(C)
endififndef KBUILD_CHECKSRCKBUILD_CHECKSRC = 0
endif
- 判断 C 是否来源于命令行,如果 C 来源于命令行,那就将 C 赋值给变量 KBUILD_CHECKSRC,如果命令行没有 C 的话 KBUILD_CHECKSRC 就为 0
模块编译(一般不用uboot编译模块,了解就行)
允许单独编译某个模块,使用命令“make M=dir”即可,旧语法“make SUBDIRS=dir”也是支持的
ifdef SUBDIRSKBUILD_EXTMOD ?= $(SUBDIRS)
endififeq ("$(origin M)", "command line")KBUILD_EXTMOD := $(M)
endif
PHONY += allifeq ($(KBUILD_EXTMOD),)_all: all
else_all: modules
endif
ifeq ($(KBUILD_SRC),)srctree := .
elseifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))srctree := ..elsesrctree := $(KBUILD_SRC)endif
endif
objtree := .
src := $(srctree)
obj := $(objtree)
VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))export srctree objtree VPATH
- 如果定义了SUBDIRS ,变量 KBUILD_EXTMOD=SUBDIRS
- 判断是否在命令行定义了 M,如果定义了的话 KBUILD_EXTMOD=$(M)
- 判断 KBUILD_EXTMOD 时为空,如果为空的话目标_all 依赖 all,因此要先编译 出 all。否则的话默认目标_all 依赖 modules,要先编译出 modules,也就是编译模块
- 判断 KBUILD_SRC 是否为空,如果为空的话就设置变量 srctree 为当前目录,即 srctree 为“.”,一般不设置 KBUILD_SRC
- 设置变量 objtree 为当前目录
- 设置变量 src 和 obj,都为当前目录
- 导出变量 scrtree、objtree 和 VPATH到子makefile
获取主机架构和系统
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/)HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \sed -e 's/\(cygwin\).*/cygwin/')export HOSTARCH HOSTOS
- 定义了一个变量 HOSTARCH,用于保存主机架构,这里调用 shell 命令“uname -m”获取架构名称
- shell 中的“|”表示管道,意思是将 左边的输出作为右边的输入
- sed -e 是替换命令
- sed -e s/i.86/x86/”表示将管道输入的字符串 中的“i.86”替换为“x86”
- 定义了变量 HOSTOS,此变量用于保存主机 OS 的值,先使用 shell 命令“uname -s”来获取主机 OS
- 使用管道将“Linux”作为后面“tr '[:upper:]' '[:lower:]'”的输入
- “tr '[:upper:]' '[:lower:]'”表示将所有的大写字母替换为小写字母
- “sed -e 's/\(cygwin\).*/cygwin/'”用于将 cygwin.*替换为 cygwin,如果主机是linux最终HOSTOS=linux
- 导出变量到子makefile
设置目标架构、交叉编译器和配置文件
编 译 uboot 的 时 候 需 要 设 置 目 标 板 架 构 和 交 叉 编 译 器 ,“ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-”就是用于设置 ARCH 和 CROSS_COMPILE
或者直接在主makefile里加入,就不需要每次编译设置了
- ARCH ?= arm
- CROSS_COMPILE ?= arm-linux-gnueabihf-
ifeq ($(HOSTARCH),$(ARCH))CROSS_COMPILE ?=
endif
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-gnueabihf-
KCONFIG_CONFIG ?= .config
export KCONFIG_CONFIG
- 行判断 HOSTARCH 和 ARCH 这两个变量是否相等,主机架构(变量 HOSTARCH)是 x86_64,而我们编译的是 ARM 版本 uboot,肯定不相等,所以 CROSS_COMPILE= arm-linuxgnueabihf-
- 定义变量 KCONFIG_CONFIG,uboot 是可以配置 的,这里设置配置文件为.config,.config 默认是没有的,需要使用命令“make xxx_defconfig” 对 uboot 进行配置,配置完成以后就会在 uboot 根目录下生成.config。
- 默认情况下.config 和 xxx_defconfig 内容是一样的,因为.config 就是从 xxx_defconfig 复制过来的。
- 如果后续自行调整 了 uboot 的一些配置参数,那么这些新的配置参数就添加到了.config 中,而不是 xxx_defconfig。 相当于 xxx_defconfig 只是一些初始配置,而.config 里面的才是实时有效的配置
包含 scripts/Kbuild.include
scripts/Kbuild.include: ;
include scripts/Kbuild.include
- 使用“include”包含了文件 scripts/Kbuild.include,此文件里面定义了很多变量,后面会用到
交叉编译工具变量设置
之前设置了交叉编译的前缀,交叉编译器其他的工具还没设置
AS = $(CROSS_COMPILE)as
ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),)LD = $(CROSS_COMPILE)ld.bfd
elseLD = $(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
导出其他变量
因为导出了很多变量,我只看几个ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR,这 7 个变量是在其他文件里面定义的
可以输出这几个变量查看,加入下面代码,运行make mytest就可以查看
mytest:@echo ‘ARCH=' $(ARCH)@echo ‘CPU =' $(CPU )@echo ‘BOARD =' $(BOARD )@echo ‘VENDOR =' $(VENDOR )@echo ‘SOC =' $(SOC )@echo ‘CPUDIR =' $(CPUDIR )@echo ‘BOARDDIR=' $(BOARDDIR)
- 在 uboot 根目录下有个文件叫做 config.mk,这 7 个变量就是在 config.mk 里面定义的,后面会包含一个子makefile,在这个makefile里会包含mk,就可以获取变量了
- 这7个值在config.mk中又是从.config里面获取的,.config是make 默认配置文件后生成的,里面有芯片,cpu,板子信息
make distclean过程
在uboot学习:(一)中的步骤1.1中,会进行清理过程,下面是分析,在主makefile搜索distclean
PHCNY += distclean
distclean: mrproper@find $(srctree) $(RCS_FIND_IGNORE) \\( -name '*.orig' -o -name '*.rej' -o -name '*~'\...-type f -print | xargs rm -f@rm -f boards.cfg
- 就是删除一些文件,所以不需要仔细看
make xxx_defconfig 过程
# 定义了变量 version_h,这变量保存版本号文件,此文件是自动生成的
version_h := include/generated/version_autogenerated.h# 定义了变量 timestamp_h,此变量保存时间戳文件,此文件也是自动生成的
timestamp_h := include/generated/timestamp_autogenerated.h# 定义了变量 no-dot-config-targets
no-dot-config-targets := clean clobber mrproper distclean \help %docs check% coccicheck \ubootversion backup# 定义了变量 config-targets,初始值为 0
config-targets := 0# 定义了变量 mixed-targets,初始值为 0
mixed-targets := 0# 定义了变量 dot-config,初始值为 1
dot-config := 1# 判断 MAKECMDGOALS 中是否包含 no-dot-config-targets 中的任何目标
# make xxx_defconfig 则MAKECMDGOALS=xxx_defconfig 则dot-config依旧为1
ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)# 判断 MAKECMDGOALS 中是否只包含 no-dot-config-targets 中的目标ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)# 如果上述条件成立,则将dot-config变量设置为0。这意味在这种情况下,不需要使用配置文件dot-config := 0endif
endif# 行判断KBUILD_EXTMOD是否为空
ifeq ($(KBUILD_EXTMOD),)# 检查 MAKECMDGOALS 是否包含以 config 开头的目标ifneq ($(filter config %config,$(MAKECMDGOALS)),)# 说明 MAKECMDGOALS 包含以 config 开头的目标,则将 config-targets 变量设置为 1config-targets := 1# 检查 MAKECMDGOALS 中的目标是否超过一个ifneq ($(words $(MAKECMDGOALS)),1)# 说明 MAKECMDGOALS 中包含不止一个目标,则将 mixed-targets 变量设置为 1mixed-targets := 1endifendif
endif# 判断是否有混合目标,如果 mixed-targets 变量的值为1,表示存在混合目标
ifeq ($(mixed-targets),1)# 将 MAKECMDGOALS 中的目标以及 __build_one_by_one 添加到伪目标(PHONY)列表中PHONY += $(MAKECMDGOALS) __build_one_by_one# 过滤出MAKECMDGOALS中除了__build_one_by_one之外的目标,并将其依赖设为 __build_one_by_one。使用@:使其成为一个空目标$(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one @:# 定义 __build_one_by_one 目标,执行以下命令__build_one_by_one:# 循环遍历 MAKECMDGOALS 中的每一个目标,并分别调用 make 进行构建$(Q)set -e; \for i in $(MAKECMDGOALS); do \$(MAKE) -f $(srctree)/Makefile $$i; \done
else# 如果 mixed-targets 不为1,则判断是否有配置目标ifeq ($(config-targets),1)# 设置默认的配置文件为 sandbox_defconfigKBUILD_DEFCONFIG := sandbox_defconfig# 导出 KBUILD_DEFCONFIG 和 KBUILD_KCONFIG 变量export KBUILD_DEFCONFIG KBUILD_KCONFIG# 定义 config 目标,其依赖为 scripts_basic、outputmakefile 和 FORCEconfig: scripts_basic outputmakefile FORCE$(Q)$(MAKE) $(build)=scripts/kconfig $@%config: scripts_basic outputmakefile FORCE$(Q)$(MAKE) $(build)=scripts/kconfig $@else# 如果 config-targets 不为1,则判断 dot-config 是否为1ifeq ($(dot-config),1)# 如果 dot-config 为1,则包含 include/config/auto.conf 文件-include include/config/auto.conf
......
分析 make xxx_defconfig
在uboot学习:(一)中的步骤1.2中,会进行默认配置make xxx_defconfig,下面是配置过程分析
我们主要看下面后面得两段代码
%config: scripts_basic outputmakefile FORCE$(Q)$(MAKE) $(build)=scripts/kconfig $@
- 依赖了scripts_basic和outputmakefile和FORCE这三个文件
FORCE
在顶层makefile中有定义,可以搜索看看
PHONY += FORCE
FORCE:
- 可以看出 FORCE 是没有规则和依赖的,所以每次都会重新生成 FORCE。当 FORCE 作为 其他目标的依赖时,由于 FORCE 总是被更新过的,因此依赖所在的规则总是会执行的
outputmakefile
在顶层makefile中有定义,可以搜索看看
PHONY += outputmakefile
outputmakefile:
# 判断 KBUILD_SRC 是否为空。如果 KBUILD_SRC 变量不为空,表示内核源码树与构建目录是分开的
ifneq ($(KBUILD_SRC),)# 创建一个符号链接 source,指向 $(srctree),其中 $(srctree) 是内核源码树的路径$(Q)ln -fsn $(srctree) source# 使用 $(CONFIG_SHELL) 运行脚本 $(srctree)/scripts/mkmakefile# 该脚本生成一个 Makefile 文件,用于在源码树和对象树之间进行构建$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \# 传递给脚本参数srctree源码树,objtree对象树,内核版本号和补丁级别VERSION和PATCHLEVEL$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif
- 定义了一个名为
outputmakefile
的伪目标,该目标主要用于在源码树与构建目录分开时,创建符号链接并生成必要的 Makefile 文件。通过这种方式,可以在构建目录中正确引用源码树,并执行内核构建过程 - 首先要判断
KBUILD_SRC
是什么,是否为空,可以输出看看是否为空,一般是为空KBUILD_SRC
:表示源码树的路径,如果为空,则表示构建在源码树内进行;如果不为空,则表示构建在源码树外进行
scripts_basic
在顶层makefile中有定义,可以搜索看看
PHONY += scripts_basic
scripts_basic:# 使用 $(MAKE) 命令进入 scripts/basic 目录进行构建$(Q)$(MAKE) $(build)=scripts/basic# 删除文件 .tmp_quiet_recordmcount$(Q)rm -f .tmp_quiet_recordmcount
# 定义一个模式规则,表示scripts/basic/目录下的所有目标都依赖于scripts_basic目标
scripts/basic/%: scripts_basic ;
- 确保在构建内核时,
scripts/basic
目录中的基本脚本总是首先被构建 - 通过定义
scripts_basic
伪目标,并设置依赖关系,可以确保这些基本脚本在构建过程中总是处于最新状态,并且在构建scripts/basic
目录下的其他目标之前已经被构建
里面最重要得就是$(Q)$(MAKE) $(build)=scripts/basic,下面是分析
- $(Q)表示是否要打印输出信息
- $(MAKE)就是make
- build在scripts/Kbuild.include文件中定义
build := -f $(srctree)/scripts/Makefile.build obj
- srctree表示当前目录,也就是 . /
- build就等于 -f ./scripts/Makefile.build obj
- 最后得到的是 make -f ./scripts/Makefile.build obj = scripts/basic
最后
最后回到 分析 make xxx_defconfig 里的一句 $(Q)$(MAKE) $(build)=scripts/kconfig $@
- $@表示目标文件,就是xxx_defconfig
- 最后展开得到的是 make -f ./scripts/Makefile.build obj = scripts/kconfig xxx_defconfig
现在我们得到了两条命令
- make -f ./scripts/Makefile.build obj = scripts/basic
- make -f ./scripts/Makefile.build obj = scripts/kconfig xxx_defconfig
第一条命令
- 在./scripts/Makefile.build中
- 会用到一个src变量,可以输出查看他的值,为scripts/basic
src := $(patsubst $(prefix)/%,%,$(obj))
- 还会遇到一串代码引用
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuilddir)/Kbuild,$(kbuild-dir)/Makefile) include $(kbuild-file)
-
将 kbuild-dir 展开后为$(if $(filter /%, scripts/basic), scripts/basic, ./scripts/basic)
-
因为没有以“/”为开头的单词,所以$(filter /%, scripts/basic)的结果为空
-
最后kbuilddir=./scripts/basic
-
-
将 kbuild-file 展开后为$(if $(wildcard ./scripts/basic/Kbuild), ./scripts/basic/Kbuild, ./scripts/basic/Makefile)
-
因为 scrpts/basic 目录中没有 Kbuild 这个文件
-
最后kbuild-file= ./scripts/basic/Makefile
-
-
最后一行就为 include ./scripts/basic/Makefile
-
也就是读取 scripts/basic 下面的 Makefile 文件
-
-
-
因为第一条命令参数只有一个参数赋值, 没有默认目标,就表示需要默认目标,就要找到__build,而__build的规则为以下代码
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target)$(extra-y)) \$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \$(subdir-ym) $(always)@:
-
其中要用到顶层makefile的变量,KBUILD_BUILTIN=1和KBUILD_MODULES=0,可以打印出来查看
-
因此展开后目标__build 为
__build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)@:
-
可以看出目标__build 有 5 个依赖:builtin-target、lib-target、extra-y、subdir-ym 和 always,可以打印这几个依赖来查看
-
最后只有always=scripts/basic/fixdep,其他都是空
-
因此__build 最终为__build: scripts/basic/fixdep
-
所以要编译出scripts/basic/fixdep这个文件(软件),对应的.c就是scripts/basic/fixdep.c,在scripts/basic/Makefile 里可以编译出来,前面已经读取了 scripts/basic/Makefile 文件,就已经编译出来了
-
- 会用到一个src变量,可以输出查看他的值,为scripts/basic
- 最后表明 scripts_basic 目标的作用就是编译出 scripts/basic/fixdep 这个软件,也就是第一条命令的作用是为了编译出这个软件,这个软件就不分析了,分析的很少,有兴趣可以了解一下
第二条命令
与第一条命令不同的是 obj = scripts/kconfig 和 xxx_defconfig
和第一条命令一样先从src开始分析,因为obj换了,所以src也会更换,对应的其他变量也变了
- 打印出来对应变化的参数或者命令,src = scripts/kconfig,kbuild-dir = ./scripts/kconfig,kbuild-file = ./scripts/kconfig/Makefile,include ./scripts/kconfig/Makefile
- 最后就关系到新的makefile,就是./scripts/kconfig/Makefile,下面分析这个makefile
- ./scripts/kconfig/Makefile
- 因为第二条命令带有目标参数xxx_defconfig,所以在makefile找%_defconfig规则
%_defconfig: $(obj)/conf $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@$(Kconfig) %_config: %_defconfig@:
- 第一行带入参数就是 xxx_defconfig: scripts/kconfig/conf,也就是xxx_defconfig依赖于scripts/kconfig/conf文件
- 这个conf是由下面一行的静默编译得来的,第二行展开就是@ scripts/kconfig/conf --defconfig=arch/../configs/xxx_defconfig Kconfig
- 因为第二条命令带有目标参数xxx_defconfig,所以在makefile找%_defconfig规则
- 最终上述命令用到了 xxx_defconfig 文件,比如 mx6ull_alientek_emmc_defconfig。这里会将 mx6ull_alientek_emmc_defconfig 中的配置输出到.config 文件中,最终生成 uboot 根目录下 的.config 文件
总结
- 先是运行make xxx_defconfig命令
- 然后就分析顶层的makefile找到文件中的%config,有依赖和命令两个
- 依赖
- scripts_basic
- make -f ./scripts/Makefile.build obj=scripts/basic
- 生成:scripts/basic/fixdep
- outputmakefile
- FORCE
- scripts_basic
- 命令
- 命令:make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
- 生成:scripts/kconfig/conf
- 命令:scripts/kconfig/conf --defconfig=arch/../configs/xxx_defconfig Kconfig
- 生成:.config
- 命令:make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
- 依赖
继续分析一下u-boot.bin生成过程
上面是分析了uboot学习:(一)中的1.1和1.2这两条命令
现在开始是uboot学习:(一)中的1.3这条命令,目的是为了生成u-boot.bin文件
# 默认目标_all
PHONY := _all
_all:PHONY += all
# 如果没有参数就默认依赖于all,KBUILD_EXTMOD表示要不要编译模块
ifeq ($(KBUILD_EXTMOD),)_all: all
else_all: modules
endifall: $(ALL-y)ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check
...
- 因为配置好 uboot 以后就可以直接 make 编译了,1.3这条命令因为只有参数V=1和-j12,所以没有指明目标,所以会使用默认目标,所以就直接在makefile里找默认目标_all
- _all默认目标有一个依赖all
- all依赖又有一个依赖$(ALL-y)
- ALL-y有多个依赖,u-boot.srec,u-boot.bin,u-boot.sym,System.map,u-boot.cfg,binary_size_check等依赖
- 我这主分析u-boot.bin,因为我们目的就为了生成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
- 因为在makefile里面没有找到CONFIG_OF_SEPARATE的定义,就知道u-boot.bin依赖于u-boot-nodtb.bin,然后命令就是$(call if_changed,copy)
- if_changed 是 一 个 函 数 , 这 个 函 数 在 scripts/Kbuild.include 中有定义,而顶层 Makefile 中会包含 scripts/Kbuild.include 文件
- 函数的作用就是从 u-boot-nodtb.bin 生成 u-boot.bin,所以我们下面开始找u-boot-nodtb.bin,先生成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,所以我们下面开始找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.lds,u-boot-main三个依赖
- 在顶层makefile中有定义u-boot-init := $(head-y)变量
- $(head-y)跟 CPU 架构有关,我们使用的是 ARM 芯片,所以 head-y 在 arch/arm/Makefile 中,head-y := arch/arm/cpu/$(CPU)/start.o
- cpu变量我们前面也知道了,所以就得到了head-y,也就得到了u-boot-init
- 在顶层makefile中有定义u-boot-main := $(libs-y)变量
- $(libs-y)在顶层 Makefile 中被定义为 uboot 所有子目录下 build-in.o 的集合
- 也就是u-boot-main被定义为 uboot 所有子目录下 build-in.o 的集合
- 找u-boot.lds的规则
u-boot.lds: $(LDSCRIPT) prepare FORCE$(call if_changed_dep,cpp_lds)
- 就相当于将以 u-boot.lds 为链接脚本,将 arch/arm/cpu/$(CPU)/start.o 和各个子目录 下的 built-in.o 链接在一起生成 u-boot
- 那现在就是找built-in.o的是怎么生成的,因为有很多个built-in.o文件,所以就拿drivers/gpio/built-in.o 为例
- 在 drivers/gpio/目录下会有个名为.built-in.o.cmd 的文件
cmd_drivers/gpio/built-in.o := arm-linux-gnueabihf-ld.bfd -r -o drivers/gpio/built-in.o drivers/gpio/mxc_gpio.o
- 可以看出drivers/gpio/built-in.o 这个文件是使用 ld 命令由文件 drivers/gpio/mxc_gpio.o 生成而来的
- mxc_gpio.o 又是 mxc_gpio.c 编译生成的.o 文件
- 这里用到了 ld 的“-r”参数,-r –relocateable: 产生可重定向的输出,比如,产生一个输出文件它可再次作为‘ld’的输 入,这经常被叫做“部分链接”,当我们需要将几个小的.o 文件链接成为一个.o 文件的时候,需 要使用此选项
- 在 drivers/gpio/目录下会有个名为.built-in.o.cmd 的文件
- 在顶层makefile中有定义u-boot-init := $(head-y)变量
- u-boot-init有一个变量head-y,最终的文件arch/arm/cpu/armv7/start.o
- 我这主分析u-boot.bin,因为我们目的就为了生成u-boot.bin文件
- 最终将各个子目录中的 built-in.o 文件链接在一起就形成了 u-boot,当使用V=1命令编译就可以看到链接的文件
- 最终是用 arm-linux-gnueabihf-ld.bfd 命令将 arch/arm/cpu/armv7/start.o 和其他众多 的 built_in.o 链接在一起,形成 u-boot
- 目标 all 除了 u-boot.bin 以外还有其他的依赖,比如 u-boot.srec 、u-boot.sym 、System.map、 u-boot.cfg 和 binary_size_check 等等,这些依赖的生成方法和 u-boot.bin 很类似
总结过程
- make第三条命令
- 匹配默认目标_all
- _all依赖all
- all依赖ALL-y
- ALL-y依赖u-boot.srec,u-boot.bin,u-boot.sym,System.map,u-boot.cfg,binary_size_check等其他配置依赖
- 我们主要获取u-boot.bin文件
- u-boot.bin依赖u-boot-nodtb.bin
- u-boot-nodtb.bin依赖u-boot
- u-boot依赖u-boot-init,u-boot.lds,u-boot-main
- u-boot-init依赖有head-y变量,head-y的变量为arch/arm/cpu/$(CPU)/start.o
- u-boot-main依赖有libs-y变量,libs-y的变量为uboot 所有子目录下 build-in.o 的集合
总结
- make xxx_defconfig
- 用于配置 uboot,这个命令最主要的目的就是生成.config 文件
- make
- 用于编译 uboot,这个命令的主要工作就是生成二进制的 u-boot.bin 文件和其他的一 些与 uboot 有关的文件,比如 u-boot.imx 等等