[ARM Linux系统移植] U-Boot make ???_defconfig 分析

本篇笔记记录 u-boot 编译过程中 make ???_defconfig 这一目标的依赖。“先修”笔记为上篇 [ARM Linux系统移植] U-Boot 顶层 Makefile 分析 - 基础知识

1. ???_defconfig 目标

首先在顶层 makefile 的第 476 行找到了 ???_defconfig 目标对应的规则,目标是用通配符进行匹配的。接下来对里面的变量和依赖进行逐一拆解。

Makefile
  1. %config: scripts_basic outputmakefile FORCE
  2.     $(Q)$(MAKE) $(build)=scripts/kconfig $@
scripts/Kbuild.include

build 变量在引入的 Kbuild.include 文件里定义,变量里指定了需要传入的 obj 变量,并且 -f 选项指定了需要读取执行的 makefile(为 scripts/Makefile.build)。

scripts/Kbuild.include
  1. ###
  2. # Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
  3. # Usage:
  4. # $(Q)$(MAKE) $(build)=dir
  5. build := -f $(srctree)/scripts/Makefile.build obj
-f makefile :

把所指定的文件(而不是默认的文件名,即 makefile、Makefile 或 GNUMakefile)作为 makefile 来读。

%config 目标的 FORCE 依赖比较简单。因为 FORCE 目标的依赖是空的,所以它总被认为是最新的,这也确保 %config 目标总是会被更新。

Makefile
  1. PHONY += FORCE
  2. FORCE:

因为默认情况下 KBUILD_SRC 变量为空,所以 %config 目标的 outputmakefile 依赖在默认情况下也没有命令需要执行。至于应有的作用,先暂不深究。

Makefile
  1. outputmakefile:
  2. ifneq ($(KBUILD_SRC),)
  3.     $(Q)ln -fsn $(srctree) source
  4.     $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
  5.         $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
  6. endif

最后只剩下 scripts_basic 依赖。scripts_basic 目标的更新同样需要执行 scripts/Makefile.build,和 %config 目标的不同之处只在于传入的 obj 变量。所以后续需要分析一下 scripts/Makefile.build 这个文件。

Makefile
  1. PHONY += scripts_basic
  2. scripts_basic:
  3.     $(Q)$(MAKE) $(build)=scripts/basic
  4.     $(Q)rm -f .tmp_quiet_recordmcount

2. Makefile.build 文件

Makefile.build 文件一开始会赋值 prefixsrc 变量。需要判断传进来的 obj 变量是否是在 tpl 或者 spl 目录下的,就第 1 节中提及的两处都不在,所以 prefix 变量为 . ,src 变量和 obj 变量内容一样。

scripts/Makefile.build
  1. prefix := tpl
  2. src := $(patsubst $(prefix)/%,%,$(obj))
  3. ifeq ($(obj),$(src))
  4. prefix := spl
  5. src := $(patsubst $(prefix)/%,%,$(obj))
  6. ifeq ($(obj),$(src))
  7. prefix := .
  8. endif
  9. endif
$(patsubst search-pattern,replace-pattern,text)

这是一个具通配符能力的“搜索和替换”函数。照例,此处的模式只可以包含一个 % 字符。replace-pattern 中的百分比符号会被扩展成与模式相符的文字。例如,下面的例子会删除 text 结尾的斜杠符号。

$(patsubst %/,%,$(directoty-path))

Makefile.build 文件内容也比较繁杂,按照开发板配套教程上的思路,把重点放在 56-59 处。如果 $(kbuild-dir)/Kbuild 文件存在则会引入它;否则引入 $(kbuild-dir)/Makefile 文件。

scripts/Makefile.build
  1. # The filename Kbuild has precedence over Makefile
  2. kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
  3. kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
  4. include $(kbuild-file)
$(wildcard pattern...)

wildcard 函数的参数是一份模式列表,它会对列表中的每个模式进行扩展的动作。如果被扩展的模式找不到相符的文件,则会返回空字符。

wildcard 的另一个用法,就是在条件语句中测试文件是否存在。例如:

$(wildcard ~/.emacs)

将会返回空字符串,如果用户的主目录中并未包含 .emacs 这个文件。

由于 make 调用 Makefile.build 的时候并没有指明目标,所以将默认使用第一个目标:__build 目标就作为默认目标。由于 __build 目标有着众多的依赖,所以这边采用运行时打印变量的方式来查看依赖,就不逐一进行分析了。图 1 为执行 make ???_defconfig 时打印的内容,可以看到基本所有变量此时都是空的,最后只有一个依赖,为 always。最终依赖的 scripts/basic/fixdep 是一个程序文件,功能未知。

scripts/Makefile.build
  1. __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
  2.      $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
  3.      $(subdir-ym) $(always)
  4.     @:
图1 打印的变量

2.1 scripts_basic 目标

如第 1 节中所见,scripts_basic 目标规则中的 make 命令传入的 obj 变量等于 scripts/basic。这时 kbuild-file 变量等于 scripts/basic/Makefile ,会引入这个文件。

2.2 %config 目标

%config 目标规则中的 make 命令传入的 obj 变量等于 scripts/kconfig。这时候 kbuild-file 变量等于 scripts/kconfig/Makefile 。make 命令指定了目标为 ???_defconfig,匹配上 scripts/kconfig/Makefile 里面的 %_defconfig 目标。

%_defconfig 目标的依赖为 scripts/kconfig/conf 程序;规则是使用 conf 程序读取指定的 ???_defconfig 文件,最终生成 .config 文件。

scripts/kconfig/Makefile
  1. %_defconfig: $(obj)/conf
  2.     $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)

3. 总结

至此执行 make ???_defconfig 命令后的顺序流程就全部梳理完毕了。整体流程如图 2 所示,矩形框里面代表的目标,可以从图中看到:最先匹配到 %config 目标;而 %config 有三个依赖,其中主要是 scripts_basic 依赖,最终需要生成 fixdep 程序。在 %config 目标所有的依赖满足之后,就会执行规则里的 make -f 命令,此时会匹配到 %defconfig 目标,最终会生成 conf 程序生成 .config 文件。

图2 整体流程