uboot if_changed函数

u-boot编译过程分析

u-boot.lds: $(LDSCRIPT) prepare FORCE
    $(call if_changed_dep,cpp_lds)

u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
    $(call if_changed,u-boot__)


有类似if_changed之类的函数出现

if_changed 定义在 scripts/Kbuild.include 中:


# Execute command if command has changed or prerequisite(s) are updated.
#
if_changed = $(if $(strip $(any-prereq) $(arg-check)),                      
    @set -e;                                                            
    $(echo-cmd) $(cmd_$(1));                                            
    printf '%s ' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd)

# 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 函数在当发现规则的依赖有更新,或者是对应目标的命令行参数发生改变时($(strip $(any-prereq) $(arg-check)) 语句结果不为空),执行后面的语句。

这里看到 arg-check  和 any-prereq 两个变量,它们分别定义在 scripts/Kbuild.include  的 188 和 200 行中:
arg-check = $(strip$(filter-out$(cmd_$(1)), $(cmd_$@))
                    $(filter-out$(cmd_$@),   $(cmd_$(1))) )
... ...
any-prereq = $(filter-out$(PHONY),$?) $(filter-out$(PHONY)$(wildcard$^),$^)
 
在 any-prereq 中,$? 表示所有比目标还要新的依赖文件;$^ 表示所有的依赖文件。

在 any-prereq 中,首先使用 $(filter-out $(PHONY),$?) 将 $? 中的所有伪目标去掉,不然可能会将 FORCE 这种目标也带进来,如果此时返回非空,那么说明有比目标还要新的依赖文件。$(wildcard $^) 匹配当前目录下的所有依赖文件(已经存在的),然后再使用 $(filter-out $(PHONY) $(wildcard $^),$^) 将伪目标以及当前目录下匹配的文件列表从整个 $^ 列表中删除,如果返回不为空,那么说明某些依赖文件不存在,也就是说这些不存在的依赖文件还没生成 -- 这是因为某些依赖文件需要在编译时才会生成。
 
在 arg-check 中,$(1) 表示第 1 个参数,比如上面的  $(call if_changed,u-boot__) 中,$(1) 就是u-boot__ ,所以 $(cmd_$(1) 就是表示 cmd_u-boot__这个变量可以在 Makefile.build 中看到定义。它实际上表示的是这一次编译文件时所用到的命令行参数。

$@ 表示目标文件,从上面叙述可知,它就是 $(obj)/%.o 。比如编译 init/main.o ,那么 $(cmd_$@) 就是表示 $(cmd_init/main.o),而在 init/.main.o.cmd 文件中我们看到了 cmd_init/main.o 用来保存着上次编译的参数。在 arg-check 中,首先使用 $(filter-out $(cmd_$(1)), $(cmd_$@)) 将上一次的编译参数中过略掉本次要编译的参数,再用 $(filter-out $(cmd_$@),   $(cmd_$(1))) 将本次的编译参数中过滤掉上一次的编译参数。正反过滤的原因是,filter-out 函数在过滤时,如果第 2 个参数是第 1 个参数的子集或者是相同,那么返回空;所以,在第 1 次过滤时如果返回为空,那么 cmd_$@ 可能是等于 cmd_$(1) 的,也可能是它的子集,所以只有当再次反过来做过滤时发现返回为空,那么才能判断两次编译的参数是相等的,否则是不等的。如果返回结果不为空,说明编译参数发生了变化,那么就会执行 $(rule_cc_o_c) 。



set -e 表示如果命令执行有错那么命令停止执行并退出。

接着 $(echo-cmd) 用来打印出相关的编译命令,接着执行 $(cmd_$(1) 里的命令。

最后 echo 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd 将上面执行的命令写入一个叫 $(dot-target).cmd 的文件中,该文件为隐藏文件,在编译后的内核源码目录及其子目录下随处可见,比如在 init/ 下可以看到 .initramfs.o.cmd, .version.o.cmd 等等。

如果目标的依赖或者命令参数已经改变,那么将执行 cmd_vmlinux__ 所代表的新命令。那么这里可能会想到,在新老命令的比较中,老命令是从何而来?正如上面所分析的,老命令正是被写入到 $(dot-target).cmd 文件中的;这样一来,在 Makefile 中肯定有一处是将这些保存了老命令的文件包含进来的语句。在顶层 Makefile 中的 1481-1489 行可以看到:

# 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
 
在目录 scripts/ 下,像在 Makefile.build、Makefile.headersinst、Makefile.modpost 以 及Makefile.fwinst 等文件中,也可以看到类似的读入语句。但这并不冲突,因为顶层 Makefile 和它们独立运作,变量间互不干扰。

原文地址:https://www.cnblogs.com/idyllcheung/p/11591962.html