|
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]
原理一样。 |
|