曲径通幽论坛

标题: filechk [打印本页]

作者: beyes    时间: 2011-7-21 11:16
标题: filechk
Makefile 对应内核版本:2.6.35.13

filechk 函数定义在 scripts/Kbuild.include 中:
[code=Makefile]
###
# filechk is used to check if the content of a generated file is updated.
# Sample usage:
# define filechk_sample
#       echo $KERNELRELEASE
# endef
# version.h : Makefile
#       $(call filechk,sample)
# The rule defined shall write to stdout the content of the new file.
# The existing file will be compared with the new one.
# - If no file exist it is created
# - If the content differ the new file is used
# - If they are equal no change, and no timestamp update
# - stdin is piped in from the first prerequisite ($<) so one has
#   to specify a valid file as first prerequisite (often the kbuild file)
define filechk
        $(Q)set -e;                             \
        $(kecho) '  CHK     $@';                \
        mkdir -p $(dir $@);                     \
        $(filechk_$(1)) < $< > $@.tmp;          \
        if [ -r $@ ] && cmp -s $@ $@.tmp; then  \
                rm -f $@.tmp;                   \
        else                                    \
                $(kecho) '  UPD     $@';        \
                mv -f $@.tmp $@;                \
        fi
endef
[/mw_shl_code]
根据注释知道,filechk 用来检查文件内容是否被更新。这里我们使用内核 Makefile 里的实例进行分析。

在内核顶层 Makefile 中可以看到:
[code=Makefile]include/linux/version.h: $(srctree)/Makefile FORCE
        $(call filechk,version.h)[/mw_shl_code]
上面的目标是 include/linux/version.h 文件,而依赖正是顶层 Makefile ,后面还跟着一个伪目标 FORCE,表示这个目标是必须要保持最新,换句话就是说每次开始编译内核时,都会检查更新  include/linux/version.h 这个文件,而不管顶层 Makefile 是原来的,还是后来改动过的。在该条规则的命令部分使用了 filecheck 函数,在上面,我们已经看到了 filecheck 函数的定义。

现在回过头来看 filecheck 函数:
$(Q)set -e;  
表示当后面的命令执行非法时会停止执行并退出。

$(kecho) '  CHK     $@';
$(kecho) 变量也定义在 scripts/Kbuild.include 中:
[code=Makefile]quiet_kecho := echo
kecho := $($(quiet)kecho)[/mw_shl_code]
其中 $(quiet) 定义在顶层 Makefile 中,在编译内核时,命令行里若不指定 V 参数,那么 $(quiet) 的值就为 quiet_ ,这样 kecho 最后就变为 echo 。这里的 $@ 目标即指代 include/linux/version.h 。如果留心内核编译的屏幕输出的话,我们可以看到:
scripts/kconfig/conf -s arch/x86/Kconfig
CHK     include/linux/version.h
  UPD     include/linux/version.h
  CHK     include/generated/utsrelease.h
  UPD     include/generated/utsrelease.h
  CC      kernel/bounds.s
  GEN     include/generated/bounds.h
  CC      arch/x86/kernel/asm-offsets.s
  GEN     include/generated/asm-offsets.h
  CALL    scripts/checksyscalls.sh
... ...
上面的 CHK     include/linux/version.h 就是在这里输出的。

mkdir -p $(dir $@);
这里建立  include/linux/ 目录,当然该目录实际上已经存在,对于用 mkdir 建立已经存在的目录并不会出现什么错误。

$(filechk_$(1)) < $< > $@.tmp;
这里 $(1) 参数就是 version.h,$(filechk_$(1)) 就是表示 filechk_version.h --- 该变量定义在顶层 Makefile 中:
[code=Makefile]define filechk_version.h
        (echo \#define LINUX_VERSION_CODE $(shell                             \
        expr $(VERSION) \* 65536 + $(PATCHLEVEL) \* 256 + $(SUBLEVEL));     \
        echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))';)
endef[/mw_shl_code]
这里用 define 和 endef 定义了一个命令包,在命令包中队内核版本的相关数值进行了计算,并 echo 了出来。这里先不管计算的具体过程,像我用的 2.6.35.13 的内核,filechk_version.h 命令包里的内容最终为:
#define LINUX_VERSION_CODE 132643
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
这些内容最终会写到 include/linux/version.h 中。

$(filechk_$(1)) < $< > $@.tmp; 这里的 $< 表示顶层 Makefile 。这里可以假设,你在编译内核之前,修改过 Makefile 文件,比如简单的修改了顶层 Makefile 中的前面几行里的内核版本号:
[code=Makefile]VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 35[/mw_shl_code]
那么新 Makefile 的内容会从输出到标准输入(< 符号表示重定向到”标准输入“)。此时,我们就从标准输入里截取了 filechk_version.h 这段命令来执行,执行的结果再重定向到 $@.tmp 文件中,该文件的名称在这里就是 include/linux/version.h.tmp 。

接着执行:
[code=Makefile]if [ -r $@ ] && cmp -s $@ $@.tmp; then  \
                rm -f $@.tmp;                   \
        else                                    \
                $(kecho) '  UPD     $@';        \
                mv -f $@.tmp $@;                \
fi[/mw_shl_code]
首先查看原来生成的 include/linux/version.h 是否可读,如果可读的话,则比较它和新生成的 include/linux/version.h.tmp 有什么不同。这里的比较使用了 cmp 命令,使用 -s 选项表示静默模式,不输出具体的比较结果,只是在返回时根据 $? 来判断比较结果是否相同。如果新旧问文件相同,那么就删除掉 include/linux/version.h.tmp 临时文件。如果不同,就在标准输出打印  UPD     include/linux/version.h 该字样,然后将临时文件仍然更名为 include/linux/version.h 。

到此整个函数结束。

另外,在顶层 Makefile 中还看到
[code=Makefile]define filechk_utsrelease.h
        if [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then \
          echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2;    \
          exit 1;                                                         \
        fi;                                                               \
        (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\";)
endef
... ...
include/generated/utsrelease.h: include/config/kernel.release FORCE
        $(call filechk,utsrelease.h)[/mw_shl_code]
原理一样。




欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) Powered by Discuz! X3.2