UBOOT编译--- make xxx_deconfig过程详解(一)


make xxx_deconfig过程详解

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

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

    展开变量build

    # 顶层Makefile
    # Basic helpers built in scripts/
    PHONY += scripts_basic
    scripts_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:顶层Makefile
    ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
    	lib-target := $(obj)/lib.a
    endif  
    

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

    (2)builtin-target ???????

    # note:顶层Makefile
    ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)
    	builtin-target := $(obj)/built-in.o
    endif
    

    在此语句之前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:顶层Makefile
    KBUILD_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 into
    subdir-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 Makefile
    kbuild-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-Boot
    prefix := tpl
    src := $(patsubst $(prefix)/%,%,$(obj))
    ifeq ($(obj),$(src))
    prefix := spl
    src := $(patsubst $(prefix)/%,%,$(obj))
    ifeq ($(obj),$(src))
    prefix := .
    endif
    endif
    

    在命令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/Makefile
    hostprogs-y	:= fixdep
    always		:= $(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.lib
    always        := $(addprefix $(obj)/,$(always))
    

    所以最终 always = scripts/basic/fixdep


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

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

    # note:scripts/Makefile.build
    ifneq ($(hostprogs-y)$(hostprogs-m),)
          include scripts/Makefile.host
    endif 
    

    在./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 file
    host-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.include
    ifneq ($(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 statements
    escsq = $(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)
    endif
    hostc_flags    = -Wp,-MD,$(depfile) $(__hostc_flags) 
    

    在scripts/Kbuild.include中:

    comma   := ,
    dot-target = $(dir $@).$(notdir $@)               //scripts/basic/.fixdep
    depfile = $(subst $(comma),_,$(dot-target).d)     //scripts/basic/.fixdep.d
    hostc_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

    # 顶层Makefile
    PHONY += FORCE
    FORCE:
    

    实际上它是一个伪目标,从上面看到,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/Makefile
    ifdef KBUILD_KCONFIG
    Kconfig := $(KBUILD_KCONFIG)
    else
    Kconfig := Kconfig
    endif
    
    %_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=""  -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=""  -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.o
    scripts/kconfig/conf  --defconfig=arch/../configs/myimx8mmek240-8mm-2g_defconfig Kconfig
    #
    # configuration written to .config
    #
    

    5 总结

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