3

UBOOT编译--- make xxx_deconfig过程详解(一) - BSP-路人甲

 1 year ago
source link: https://www.cnblogs.com/jianhua1992/p/16852775.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
 UBOOT版本:uboot2018.03,开发板myimx8mmek240。

Ubootb编译第一步通常是执行make xxx_config,在编译指定顶层目录生成.config文件,这种方式要求厂商提供一个基础的xxx_config文件(通常来说开发者不会通过执行make menuconfig从零开始配置,这个工作过量太大了)。本文接下来的章节主要解析这条指令背后主要做了什么。我是用的开发板执行命令为:make myimx8mmek240-8mm-2g_defconfig

3. build变量的定义

在scripts/Kbuild.include 中定义:

#### Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=# Usage:# $(Q)$(MAKE) $(build)=dirbuild := -f $(srctree)/scripts/Makefile.build obj

4. 目标%config的定义

在顶层Makefile中定义:

# We need some generic definitions (do not try to remake the file).scripts/Kbuild.include: ;include scripts/Kbuild.include //注意这个引用 ...... config: scripts_basic outputmakefile FORCE $(Q)$(MAKE) $(build)=scripts/kconfig $@ %config: scripts_basic outputmakefile FORCE $(Q)$(MAKE) $(build)=scripts/kconfig $@

4.1 依赖 scripts_basic

(参考:linux内核Makefile中的变量build— 过渡篇(五)

# 顶层Makefile# Basic helpers built in scripts/PHONY += scripts_basicscripts_basic: $(Q)$(MAKE) $(build)=scripts/basic $(Q)rm -f .tmp_quiet_recordmcount

展开变量build

# 顶层Makefile# Basic helpers built in scripts/PHONY += scripts_basicscripts_basic: $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.build obj=scripts/basic $(Q)rm -f .tmp_quiet_recordmcount
 make -f $(srctree)/scripts/Makefile.build obj=scripts/basic的解析如下:

这是一种不指定目标的情况,由于未指定目标,这时会使用Makefile.build中的默认目标__build。然后更进一步,会使用$(obj)/Makefile(scripts/basic/Makefile)中定义的变量来进行目标匹配。

__build在Makefile.build中的构建规则为:

__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \ $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \ $(subdir-ym) $(always) @:

4.1.1 语句$(if $ (KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y))

在顶层Makefile中,KBUILD_BUILTIN的定义如下:

# note:顶层Makefile KBUILD_BUILTIN := 1 export KBUILD_MODULES KBUILD_BUILTIN

该语句展开为:

$(builtin-target) $(lib-target) $(extra-y)

(1)lib-target

# note:顶层Makefileifneq ($(strip $(lib-y) $(lib-m) $(lib-)),) lib-target := $(obj)/lib.aendif

在此语句之前obj-y := ;obj-m := ;obj-未定义。因此lib-target为空

(2)builtin-target ​​​​​​​

# note:顶层Makefileifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),) builtin-target := $(obj)/built-in.oendif

在此语句之前obj-y := ; obj-m := ;obj-未定义 ; subdir-m := ;并且在所包含的文件中也没有给这些变量增加值。lib-target 为空。因此builtin-target为空

(3)extra-y未定义
综上:语句$(if $ (KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y))为空

4.1.2 语句 $(if $ (KBUILD_MODULES),$(obj-m) $(modorder-target))*

在顶层Makefile中,KBUILD_BUILTIN的定义如下:

# note:顶层MakefileKBUILD_MODULES :=

综上:语句$(if $ (KBUILD_MODULES),$(obj-m) $(modorder-target))为空

4.1.3 $(subdir-ym)

在scripts/Makefile.lib中,subdir-ym的定义如下:

# note:scripts/Makefile.lib# Subdirectories we need to descend intosubdir-ym := $(sort $(subdir-y) $(subdir-m))......subdir-ym := $(addprefix $(obj)/,$(subdir-ym))

subdir-y与subdir-m都为空。
综上:语句$(subdir-ym)为空

4.1.4 $(always) 重点关注

在scripts/Makefile.build有如下定义

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

4.1.4.1 src的定义

在scripts/Makefile.build有如下定义

# note:scripts/Makefile.build# Modified for U-Bootprefix := tplsrc := $(patsubst $(prefix)/%,%,$(obj))ifeq ($(obj),$(src))prefix := splsrc := $(patsubst $(prefix)/%,%,$(obj))ifeq ($(obj),$(src))prefix := .endifendif

在命令make -f ./scripts/Makefile.build obj=scripts/basic我们传入了obj=scripts/basic
所以src = obj = scripts/basic, prefix := .

4.1.4.2 kbuild-dir的定义

在scripts/Makefile.build有如下定义

kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))

所以 kbuild-dir = ./scripts/basic

4.1.4.3 kbuild-file的定义

在scripts/Makefile.build有如下定义

kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)

所以kbuild-file = ./scripts/basic/Makefile

所以include $(kbuild-file)即为include ./scripts/basic/Makefile

在./scripts/basic/Makefile中:

# note:scripts/basic/Makefilehostprogs-y := fixdepalways := $(hostprogs-y) # fixdep is needed to compile other host programs$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep

所以always = fixdep

4.1.4.4 又在Makefile.build中包含include scripts/Makefile.lib

而在scripts/Makefile.lib中

# note:scripts/Makefile.libalways := $(addprefix $(obj)/,$(always))

所以最终 always = scripts/basic/fixdep

故__build 展开如下:
__build: scripts/basic/fixdep
@:

1. 在Makefile.build中有如下定义:

# note:scripts/Makefile.buildifneq ($(hostprogs-y)$(hostprogs-m),) include scripts/Makefile.hostendif

在./scripts/basic/Makefile中hostprogs-y = fixdep, 所以scripts/Makefile.host被包含进Makefile.build;


在scripts/Makefile.host中

# note:scripts/Makefile.host__hostprogs := $(sort $(hostprogs-y) $(hostprogs-m))

hostprogs-m为空,所以__hostprogs = fixdep。


在scripts/Makefile.host中

# note:scripts/Makefile.host# C code# Executables compiled from a single .c filehost-csingle := $(foreach m,$(__hostprogs), \ $(if $($(m)-objs)$($(m)-cxxobjs)$($(m)-sharedobjs),,$(m)))......host-csingle := $(addprefix $(obj)/,$(host-csingle))

因为fixdep-objs与fixdep-cxxobjs都不存在,所以host-csingle = fixdep; 又 host-csingle := $ (addprefix $ (obj)/,$(host-csingle)),所以host-csingle = scripts/basic/fixdep


host-csingle规则如下:

# note:scripts/Makefile.host$(host-csingle): $(obj)/%: $(src)/%.c FORCE $(call if_changed_dep,host-csingle)
scripts/basic/fixdep:scripts/basic/fixdep.c FORCE $(call if_changed_dep,host-csingle)

2. if_changed_dep在scripts/Kbuild.include中定义

# note:scripts/Kbuild.include# Execute the command and also postprocess generated .d dependencies file.if_changed_dep = $(if $(strip $(any-prereq) $(arg-check) ), \ @set -e; \ $(echo-cmd) $(cmd_$(1)); \ scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp;\ rm -f $(depfile); \ mv -f $(dot-target).tmp $(dot-target).cmd)

2.1 $(strip $(any-prereq) $(arg-check) )
(1) any-prereq在scripts/Kbuild.include中定义

# note:scripts/Kbuild.include any-prereq = $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^)

$ ? 表示所有比目标还要新的依赖文件;$ ^ 表示所有的依赖文件,$(filter-out $ (PHONY), $?)就是过滤到比目标还要新的依赖文件中的伪目标,即为 scripts/basic/fixdep.c, $ (filter-out $ (PHONY) $ (wildcard $ ^ ), $^)表示过滤掉所有的依赖文件中的伪目标与存在的依赖文件,这里为空,所以any-prereq = scripts/basic/fixdep.c


(2) arg-check在scripts/Kbuild.include中定义:

# note:scripts/Kbuild.includeifneq ($(KBUILD_NOCMDDEP),1) arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ $(filter-out $(cmd_$@), $(cmd_$(1))) )else arg-check = $(if $(strip $(cmd_$@)),,1)endif


KBUILD_NOCMDDEP是在make命令行中定义,我们并没有定义,所以:

arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) $(filter-out $(cmd_$@), $(cmd_$(1))) )

<1> $ (filter-out $ (cmd_ $ (1)), $ (cmd_ $@)) 表示过滤掉 $(cmd_ $@)中符合 $(cmd_ $(1))的项。 $(1)表示if_changed_dep函数的第一个参数host-csingle, $@表示目标文件scripts/basic/fixdep。
<2> cmd_scripts/basic/fixdep并没有定义,所以 $(filter-out $(cmd_ $(1)), $(cmd_ $@))为空;
<3> cmd_host-csingle 在Makefile.host中定义:

cmd_host-csingle = $(HOSTCC) $(hostc_flags) -o $@ $< $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))

所以arg-check = $ (filter-out $ (cmd_$ @), $ (cmd_$ (1))) = $ (HOSTCC) $ (hostc_flags) -o $@ $< $(HOST_LOADLIBES) $(HOSTLOADLIBES_ $(@F))

$(any-prereq) $(arg-check)都为非空,所以:

if_changed_dep = @set -e; \ /如果任何语句的执行结果不是true则应该退出 $(echo-cmd) $(cmd_$(1)); \ scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp;\ rm -f $(depfile); \ mv -f $(dot-target).tmp $(dot-target).cmd)

2.2 $(echo-cmd) $(cmd_ $ (1))等价于$(echo-cmd) $(cmd_host-csingle)

echo-cmd = $(if $( $(quiet)cmd_$(1)),echo ' $(call escsq, $( $(quiet)cmd_ $(1))) $(echo-why)';)

quiet=quiet_,在顶层Makefile分析过(当然如果你想看到更详细的打印,您可以通过传入V值,来改变), $(cmd_host-csingle)上面分析过,存在,所以:

echo-cmd = echo ' $(call escsq,$(cmd_host-csingle))$(echo-why)';

在scripts/Kbuild.include中:

# Escape single quote for use in echo statementsescsq = $(subst $(squote),'\$(squote)',$1)ifeq ($(KBUILD_VERBOSE),2)why = \ $(if $(filter $@, $(PHONY)),- due to target is PHONY, \ $(if $(wildcard $@), \ $(if $(strip $(any-prereq)),- due to: $(any-prereq), \ $(if $(arg-check), \ $(if $(cmd_$@),- due to command line change, \ $(if $(filter $@, $(targets)), \ - due to missing .cmd file, \ - due to $(notdir $@) not in $$(targets) \ ) \ ) \ ) \ ), \ - due to target missing \ ) \ ) echo-why = $(call escsq, $(strip $(why)))endif

KBUILD_VERBOSE一般我们会采用默认值0(需要调试编译除外),所以 echo-why 为空。

quiet_cmd_host-csingle = HOSTCC $@ //用来打印cmd_host-csingle = '$(HOSTCC) $(hostc_flags) -o $@ $< $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))'

$(HOSTCC)为cc,此处不再深入解释,hostc_flags在Makefile.host中定义:

###### Handle options to gcc. Support building with separate output directory _hostc_flags = $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) \ $(HOSTCFLAGS_$(basetarget).o) //-Wall -Wstrict-prototypes -O2 -fomit-frame-pointer _hostcxx_flags = $(HOSTCXXFLAGS) $(HOST_EXTRACXXFLAGS) \ $(HOSTCXXFLAGS_$(basetarget).o)ifeq ($(KBUILD_SRC),) //KBUILD_SRC在make命令行定义,此处未定义__hostc_flags = $(_hostc_flags)__hostcxx_flags = $(_hostcxx_flags)else__hostc_flags = -I$(obj) $(call flags,_hostc_flags)__hostcxx_flags = -I$(obj) $(call flags,_hostcxx_flags)endifhostc_flags = -Wp,-MD,$(depfile) $(__hostc_flags)

在scripts/Kbuild.include中:

comma := ,dot-target = $(dir $@).$(notdir $@) //scripts/basic/.fixdepdepfile = $(subst $(comma),_,$(dot-target).d) //scripts/basic/.fixdep.dhostc_flags = -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
综上
cmd_host-csingle = cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -o scripts/basic/fixdep scripts/basic/fixdep.c

所以echo-cmd 的作用是打印quiet_cmd_host-csingle(HOSTCC $ @ )或者cmd_host-csingle(根据顶层MakefileV值决定),$ (cmd_$(1))即为执行cmd_host-csingle生成fixdep同时生成fixdep的依赖文件.fixdep.d

(2.3) scripts/basic/fixdep $(depfile) $@ ’ $(make-cmd)’ > $(dot-target).tmp
等价于:scripts/basic/fixdep scripts/basic/.fixdep.d scripts/basic/fixdep ‘cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -o scripts/basic/fixdep scripts/basic/fixdep.c’ > scripts/basic/.fixdep.tmp

(2.4) rm -f $(depfile)
删除scripts/basic/.fixdep.d

(2.5) mv -f $(dot-target).tmp $(dot-target).cmd)
将scripts/basic/.fixdep.tmp重命名为scripts/basic/.fixdep.cmd

总结:生成scripts/basic/fixdep的过程中会先打印cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -o scripts/basic/fixdep scripts/basic/fixdep.c同时执行该语句生成fixdep,再用fixdep生成.fixdep.cmd

4.2 依赖 outputmakefile

# 顶层Makefile# outputmakefile generates a Makefile in the output directory, if using a# separate output directory. This allows convenient use of make in the# output directory.outputmakefile:ifneq ($(KBUILD_SRC),) $(Q)ln -fsn $(srctree) source $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \ $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)endif

上面批注已经说的很清楚了,outputmakefile 在输出目录中生成一个 Makefile,如果使用单独的输出目录。 这允许在输出目录中方便地使用 make。当KBUILD_SRC不为空时,才会编译到这里。

4.3 依赖 FORCE

# 顶层MakefilePHONY += FORCEFORCE:

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

4.4 规则 $ (Q)$(MAKE) $(build)=scripts/kconfig $@

等价于:make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig myimx8mmek240-8mm-2g_defconfig

在scripts/kconfig/Makefile中(至于为什么会引用scripts/kconfig/Makefile,参见4.1小节)

# note:scripts/kconfig/Makefileifdef KBUILD_KCONFIGKconfig := $(KBUILD_KCONFIG)elseKconfig := Kconfigendif %_defconfig: $(obj)/conf $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) //$< = $(obj)/conf # Added for U-Boot (backward compatibility)%_config: %_defconfig @:

编译的流程为:
(1)先编译scripts/kconfig/conf可执行文件;
(2)再执行scripts/kconfig/conf --defconfig=arch/…/configs/myimx8mmek240-8mm-2g_defconfig Kconfig语句

编译打印如下

make -f ./scripts/Makefile.build obj=scripts/kconfig myimx8mmek240-8mm-2g_defconfig cc -Wp,-MD,scripts/kconfig/.conf.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -I/usr/include/ncursesw -DCURSES_LOC="<curses.h>" -DNCURSES_WIDECHAR=1 -DLOCALE -c -o scripts/kconfig/conf.o scripts/kconfig/conf.c cat scripts/kconfig/zconf.tab.c_shipped > scripts/kconfig/zconf.tab.c cat scripts/kconfig/zconf.lex.c_shipped > scripts/kconfig/zconf.lex.c cat scripts/kconfig/zconf.hash.c_shipped > scripts/kconfig/zconf.hash.c cc -Wp,-MD,scripts/kconfig/.zconf.tab.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -I/usr/include/ncursesw -DCURSES_LOC="<curses.h>" -DNCURSES_WIDECHAR=1 -DLOCALE -Iscripts/kconfig -c -o scripts/kconfig/zconf.tab.o scripts/kconfig/zconf.tab.c cc -o scripts/kconfig/conf scripts/kconfig/conf.o scripts/kconfig/zconf.tab.oscripts/kconfig/conf --defconfig=arch/../configs/myimx8mmek240-8mm-2g_defconfig Kconfig## configuration written to .config#

经过前面的分析可知:当执行make xxx_deconfig时,最终会执行以下两个语句:
(1)make -f $(srctree)/scripts/Makefile.build obj=scripts/basic
(2)make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_deconfig

在这里插入图片描述

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK