【u-boot-2018.11】make工具之fixdep

1. 概述

fixdep工具的源码位于scripts/basic/fixdep.c中,代码本身并不复杂,但其作用是什么?哪里调用?如何调用?请看下面一一道来。

2. 哪里调用

直接在u-boot源码目录下搜索:

命令:
      grep -rnw fixdep . --exclude-dir=basic
结果:
      ./scripts/Makefile.build:270:	scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' >    \
      ./scripts/Kbuild.include:230:# if_changed_dep  - as if_changed, but uses fixdep to reveal dependencies
      ./scripts/Kbuild.include:267:	scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp;\
      ./board/bosch/shc/README:30:  HOSTCC  scripts/basic/fixdep
      匹配到二进制文件 ./.git/index

grep参数说明:

-r,递归搜索目录

-n,显示匹配结果的行号

-w,按单词搜索

--exclude-dir=basic,忽略basic子目录,此目录是fixdep源码所在目录

总共有5条匹配结果,只有第1条./scripts/Makefile.build的270行和第3条./scripts/Kbuild.include的267行调用了fixdep,其他的与之无关。

2.1 第1条匹配结果:./scripts/Makefile.build的270行

该匹配结果的详细信息:

define rule_cc_o_c
	$(call echo-cmd,checksrc) $(cmd_checksrc)			  \
	$(call echo-cmd,cc_o_c) $(cmd_cc_o_c);				  \
	$(cmd_modversions)						  \
	$(call echo-cmd,record_mcount)					  \
	$(cmd_record_mcount)						  \
	scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' >    \
	                                              $(dot-target).tmp;  \
	rm -f $(depfile);						  \
	mv -f $(dot-target).tmp $(dot-target).cmd
endef

从以上可以看出,自定义宏rule_cc_o_c调用fixdep。

进一步搜索对rule_cc_o_c的调用:

命令:
      grep -rnw rule_cc_o_c .
结果:
      ./scripts/Makefile.build:199:# (See cmd_cc_o_c + relevant part of rule_cc_o_c)
      ./scripts/Makefile.build:264:define rule_cc_o_c

结果显示除了定义之外,并没有显示rule_cc_o_c被引用。那换一种方式,搜索cc_o_c:

命令:
      grep -rnw cc_o_c .
结果:
      ./scripts/Makefile.build:266:	$(call echo-cmd,cc_o_c) $(cmd_cc_o_c);				  \
      ./scripts/Makefile.build:270:	scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' >    \
      ./scripts/Makefile.build:279:	$(call if_changed_rule,cc_o_c)
      ./scripts/Makefile.build:285:	$(call if_changed_rule,cc_o_c)
      ./scripts/Makefile.lib:393:	$(call if_changed_rule,cc_o_c)
      ./examples/api/Makefile:67:	$(call if_changed_rule,cc_o_c)

得到6条搜索结果,其前两条实际上是宏rule_cc_o_c定义本身,最后一条是u-boot例子里面的调用,也可以忽略。重点是中间三条:

第3条:
# Built-in and composite module parts
$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
	$(call cmd,force_checksrc)
	$(call if_changed_rule,cc_o_c)

第4条:
# Single-part modules are special since we need to mark them in $(MODVERDIR)
$(single-used-m): $(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
	$(call cmd,force_checksrc)
	$(call if_changed_rule,cc_o_c)
	@{ echo $(@:.o=.ko); echo $@; } > $(MODVERDIR)/$(@F:.o=.mod)

第5条:
$(obj)/efi_reloc.o: $(srctree)/arch/$(ARCH)/lib/$(EFI_RELOC:.o=.c) $(recordmcount_source) FORCE
	$(call cmd,force_checksrc)
	$(call if_changed_rule,cc_o_c)

很显然,第3条是编译%.c生成%.o时使用的规则;

而第4条中的$(simple-used-m)规则从字面上看是用于生成.ko文件(模块文件)的,而u-boot并不生成.ko文件(模块文件),所以实际上用于编译%.c生成%.o的规则是第3条显示的那条规则;

第5条的规则与第3条的规则是一样的。

2.2 第3条匹配结果:./scripts/Kbuild.include的267行

该匹配结果的详细信息:

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

从上面可以看出自定义函数if_changed_dep调用了fixdep。

进一步搜索对if_changed_dep的调用:

命令:
      grep -rnw if_changed_dep .
结果:
      ./scripts/Makefile.host:97:	$(call if_changed_dep,host-csingle)
      ./scripts/Makefile.host:114:	$(call if_changed_dep,host-cobjs)
      ./scripts/Makefile.host:131:	$(call if_changed_dep,host-cxxobjs)
      ./scripts/Makefile.build:173:	$(call if_changed_dep,cc_s_c)
      ./scripts/Makefile.build:179:	$(call if_changed_dep,cc_i_c)
      ./scripts/Makefile.build:294:	$(call if_changed_dep,cc_lst_c)
      ./scripts/Makefile.build:308:	$(call if_changed_dep,as_s_S)
      ./scripts/Makefile.build:314:	$(call if_changed_dep,as_o_S)
      ./scripts/Makefile.build:326:	$(call if_changed_dep,cpp_lds_S)
      ./scripts/Makefile.lib:310:	$(call if_changed_dep,dtc)
      ./scripts/Makefile.lib:330:	$(call if_changed_dep,dtco)
      ./scripts/Makefile.lib:389:	$(call if_changed_dep,as_o_S)
      ./scripts/Makefile.spl:377:	$(call if_changed_dep,cpp_lds)
      ./scripts/Kbuild.include:230:# if_changed_dep  - as if_changed, but uses fixdep to reveal dependencies
      ./scripts/Kbuild.include:264:if_changed_dep = $(if $(strip $(any-prereq) $(arg-check) ),                  \
      ./Kbuild:44:	$(call if_changed_dep,cc_s_c)
      ./Kbuild:65:	$(call if_changed_dep,cc_s_c)
      ./dts/Makefile:53:	$(call if_changed_dep,as_o_S)
      ./examples/api/Makefile:71:	$(call if_changed_dep,as_o_S)
      ./arch/sandbox/cpu/Makefile:20:	$(call if_changed_dep,cc_os.o)
      ./arch/sandbox/cpu/Makefile:22:	$(call if_changed_dep,cc_os.o)
      ./arch/sandbox/cpu/Makefile:31:	$(call if_changed_dep,cc_eth-raw-os.o)
      ./arch/arm/mach-imx/Makefile:82:	$(call if_changed_dep,as_o_S)
      ./arch/arm/mach-imx/Makefile:102:	$(call if_changed_dep,cpp_cfg)
      ./Makefile:1294:	$(call if_changed_dep,cpp_lds)
      ./Makefile:1532:	$(call if_changed_dep,cpp_lds)

检查以上所有对if_changed_dep调用的地方,都有一个共同点:那就是新生成一个目标时,调用if_changed_dep去生成它的依赖文件。

例如:

  • ./scripts/Makefile.host:97:    $(call if_changed_dep,host-csingle)
$(host-csingle): $(obj)/%: $(src)/%.c FORCE
	$(call if_changed_dep,host-csingle)

该规则是将%.c编译输出为可执行文件%。

  • ./scripts/Makefile.build:314:    $(call if_changed_dep,as_o_S)
$(obj)/%.o: $(src)/%.S FORCE
	$(call if_changed_dep,as_o_S)

该规则是将%.S编译输出为%.o。

  • ./scripts/Makefile.lib:310:    $(call if_changed_dep,dtc)
$(obj)/%.dtb: $(src)/%.dts FORCE
	$(call if_changed_dep,dtc)

该规则是将%.dts编译输出为%.dtb。

以上所有对if_changed_dep调用的地方,还有另外一个共同点,也可以说if_changed_dep的调用也还有另外一个特别的地方,那就是除了Makefile.host中调用if_changed_dep,是将%.c编译输出为%.o外,还没有其他的调用是用于编译%.c文件的。

注:Makefile.host在Kbuild体系中,用于编译在主机上运行的程序,而不是编译u-boot自身的文件。

2.3 fixdep调用结论

从前两节的分析看,以下情况会调用fixdep进行处理:

  • 将生成u-boot的%.c编译输出为%.o是会调用rule_cc_o_c,rule_cc_o_c又会调用fixdep生成%.o的依赖文件.%.cmd;
  • 新生成一个目标时,调用if_changed_dep检测并更新依赖文件,其中if_changed_dep会调用fixdep去处理依赖文件,但是将用于生成u-boot的%.c文件编译输出为%.o的规则除外;

3. 如何调用

通过fixdep.c的usage()函数,可以看到fixdep的用法:

Usage: fixdep   

fixdep接收三个参数,分别是:

  • :编译产生的依赖文件*.d
  • :编译生成的目标
  • :编译使用的命令

4. 输入和输出

首先先看一下调用if_changed_dep的实例:

修改if_changed_dep,打印调用fixdep的命令,并保留原有的依赖文件:

# 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));                                             \
	echo 'call fixdep: scripts/basic/fixdep $(depfile) $@ "$(make-cmd)"';\
	scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp;\
	mv -f $(dot-target).tmp $(dot-target).cmd)

重新执行make pangu_basic_defconfig,得到如下log:

wei@wei-PC:~/stm32mp/uboot/u-boot-st$ make pangu_basic_defconfig
  HOSTCC  scripts/basic/fixdep
call fixdep: scripts/basic/fixdep scripts/basic/.fixdep.d scripts/basic/fixdep "cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer  -std=gnu11     -o scripts/basic/fixdep scripts/basic/fixdep.c  "
  HOSTCC  scripts/kconfig/conf.o
call fixdep: scripts/basic/fixdep scripts/kconfig/.conf.o.d scripts/kconfig/conf.o "cc -Wp,-MD,scripts/kconfig/.conf.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer  -std=gnu11   -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DCURSES_LOC="" -DNCURSES_WIDECHAR=1 -DLOCALE   -c -o scripts/kconfig/conf.o scripts/kconfig/conf.c"
  YACC    scripts/kconfig/zconf.tab.c
  LEX     scripts/kconfig/zconf.lex.c
  HOSTCC  scripts/kconfig/zconf.tab.o
call fixdep: scripts/basic/fixdep scripts/kconfig/.zconf.tab.o.d scripts/kconfig/zconf.tab.o "cc -Wp,-MD,scripts/kconfig/.zconf.tab.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer  -std=gnu11   -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DCURSES_LOC="" -DNCURSES_WIDECHAR=1 -DLOCALE  -Iscripts/kconfig -c -o scripts/kconfig/zconf.tab.o scripts/kconfig/zconf.tab.c"
  HOSTLD  scripts/kconfig/conf
#
# configuration written to .config
#

从log可以看出,执行make pangu_basic_defconfig一共调用了3次fixdep,以处理scripts/basic/.fixdep.d为例,调用fixdep命令的参数分别为:

  • depfile  =  scripts/basic/.fixdep.d
  • target  =  scripts/basic/fixdep
  • cmdline  =  cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer  -std=gnu11     -o scripts/basic/fixdep scripts/basic/fixdep.c  

4.1 输入和输出

编译过程中生成的依赖文件.fixdep.d作为输入,经过处理得到.fixdep.cmd文件,对比其中的变化:

原来的依赖文件.fixdep.d:

fixdep.o: scripts/basic/fixdep.c /usr/include/stdc-predef.h \
 ... ... /* 若干头文件 */
 /usr/include/x86_64-linux-gnu/bits/in.h

新生成的依赖文件.fixdep.cmd:

cmd_scripts/basic/fixdep := cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer  -std=gnu11     -o scripts/basic/fixdep scripts/basic/fixdep.c  

source_scripts/basic/fixdep := scripts/basic/fixdep.c

deps_scripts/basic/fixdep := \
    $(wildcard include/config/his/driver.h) \
    $(wildcard include/config/my/option.h) \
    $(wildcard include/config/.h) \
    $(wildcard include/config/foo.h) \
    $(wildcard include/config/boom.h) \
    $(wildcard include/config/....h) \
    $(wildcard include/config/is.h) \
  /usr/include/stdc-predef.h \
  ... ... /* 若干头文件 */
  /usr/include/x86_64-linux-gnu/bits/in.h \

scripts/basic/fixdep: $(deps_scripts/basic/fixdep)

$(deps_scripts/basic/fixdep):

与原来的.fixdep.d相比,新生成的.fixdep.cmd增加了跟目标相关的变量cmd_xxx、source_xxx、deps_xxx和目标xxx对dep_xxx的依赖规则。

4.2 .*.cmd的引用

顶层Makefile中是这样引用.*.cmd的:

# read all saved command lines

targets := $(wildcard $(sort $(targets)))
cmd_files := $(wildcard .*.cmd $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd))

ifneq ($(cmd_files),)
  $(cmd_files): ;	# Do not try to update included dependency files
  include $(cmd_files)
endif

根据待生成的目标targets生成cmd_lines列表,然后用include指令包含所有的.*.cmd文件。

以生成的arch/arm/cpu/armv7/.start.o.cmd为例,文件中包含:

cmd_arch/arm/cpu/armv7/start.o := arm-openstlinux_eglfs-linux-gnueabi-gcc ... -c -o arch/arm/cpu/armv7/start.o arch/arm/cpu/armv7/start.S

source_arch/arm/cpu/armv7/start.o := arch/arm/cpu/armv7/start.S

deps_arch/arm/cpu/armv7/start.o := \
    $(wildcard include/config/armv7/lpae.h) \
    $(wildcard include/config/omap44xx.h) \
    ... ...

arch/arm/cpu/armv7/start.o: $(deps_arch/arm/cpu/armv7/start.o)

$(deps_arch/arm/cpu/armv7/start.o):

实际上只使用到依赖规则:

deps_arch/arm/cpu/armv7/start.o := \
    $(wildcard include/config/armv7/lpae.h) \
    $(wildcard include/config/omap44xx.h) \
    ... ...

arch/arm/cpu/armv7/start.o: $(deps_arch/arm/cpu/armv7/start.o)

$(deps_arch/arm/cpu/armv7/start.o):

对于cmd_arch/arm/cpu/armv7/start.o和source_arch/arm/cpu/armv7/start.o,u-boot编译时并没有用到。

至于cmd_xxx和source_xxx,能够想到的用处就是存放一些编译用到的信息,例如cmd_xxx表示生成所用的命令,source_xxx表示生成目标的source code,如果想看某一个文件是如何生成的,去查看.*.cmd文件就行了。其实这也是蛮有用的。

例如,想查看u-boot.bin是如何生成的,那就直接去查.u-boo.bin.cmd,一目了然:

cmd_u-boot.bin := cp u-boot-dtb.bin u-boot.bin

哎呦,u-boot.bin竟然是直接cp u-boot.dtb.bin来的。

5. 总结

上面几部分主要表述了整个分析的过程,下面做总结:

编译时,编译器会根据选项-MD自动生成依赖文件.*.d,用fixdep更新.*.d文件生成新的依赖文件.*.cmd。

fixdep被两个地方调用:

  • rule_cc_o_c:编译u-boot自身的%.c文件时,rule_cc_o_c调用fixdep去更新生成的%.c的依赖文件.%.cmd。
  • if_changed_dep:适用于除了上述的rule_cc_o_c外的其他目标依赖文件的生成,例如生成主机上执行的程序、处理dts文件、处理汇编文件生成*.o(或生成*.s、*.lst)等。

通过查看fixdep输出的.*.cmd文件,可以知道对应的目标*是如何生成的。

你可能感兴趣的:(u-boot)