《Linux内核Makefile分析》之 if_changed_rule/cc_o_c/any-prereq/arg-check
2015-12-10 14:35
639 查看
http://blog.sina.com.cn/s/blog_87c063060101l25q.html
if_changed_rule/cc_o_c/any-prereq/arg-check
Makefile 对应内核版本:2.6.35.13
在 scripts/Makefile.build 的 86 行看到 builtin-target 的定义:
builtin-target 是一个 builtin-in.o 文件。在 297 行中看到:
由此可见 builtin-in.o 文件依赖于各个obj-y 。obj-y 定义在要编译的子目录的 Makefile 中,这些 Makefile 文件在 Makefile.build 的 43-44 行做了包含:
比如在 init 目录下可以看到 obj-y 的一行定义:
上面的这些 .o 文件又由 Makefile.build 中的 229-231 这几行的规则编译而成:
实际上就是将对应的 .c 文件编译成 .o 文件。如果都所有的 .c 文件都已经编译为相应的 .o 文件,那么就会由 Makefile.build 中的第 298 行命令将这些 .o 文件链接成
built-in.o 文件。
这里先分析 if_changed_rule 和 cc_o_c 的实现:
if_changed_rule 定义在 scripts/Kbuild.include 的 220-222 行中:
这里看到 arg-check和 any-prereq 两个变量,它们分别定义在
scripts/Kbuild.include的 188 和 200 行中:
在 any-prereq 中,$? 表示所有比目标还要新的依赖文件;$^ 表示所有的依赖文件。
在 any-prereq 中,首先使用 $(filter-out $(PHONY),$?) 将 $? 中的所有伪目标去掉,不然可能会将 FORCE 这种目标也带进来,如果此时返回非空,那么说明有比目标还要新的依赖文件。$(wildcard
$^) 匹配当前目录下的所有依赖文件(已经存在的),然后再使用 $(filter-out $(PHONY) $(wildcard $^),$^) 将伪目标以及当前目录下匹配的文件列表从整个 $^ 列表中删除,如果返回不为空,那么说明某些依赖文件不存在,也就是说这些不存在的依赖文件还没生成 -- 这是因为某些依赖文件需要在编译时才会生成。
在 arg-check 中,$(1) 表示第 1 个参数,比如上面的 $(call if_changed_rule,cc_o_c) 中,$(1) 就是 cc_o_c ,所以 $(cmd_$(1) 就是表示 cmd_cc_o_c -- 这个变量可以在 Makefile.build 中的 179 或 194 行看到定义。它实际上表示的是这一次编译文件时所用到的命令行参数。
$@ 表示目标文件,从上面叙述可知,它就是 $(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) 。
if_changed 定义在 scripts/Kbuild.include 中:
?
if_changed 函数在当发现规则的依赖有更新,或者是对应目标的命令行参数发生改变时($(strip $(any-prereq) $(arg-check)) 语句结果不为空),执行后面的语句。
set -e 表示如果命令执行有错那么命令停止执行并退出。
接着 $(echo-cmd) 用来打印出相关的编译命令,接着执行 $(cmd_$(1) 里的命令。
最后 echo 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd 将上面执行的命令写入一个叫 $(dot-target).cmd 的文件中,该文件为隐藏文件,在编译后的内核源码目录及其子目录下随处可见,比如在 init/ 下可以看到 .initramfs.o.cmd, .version.o.cmd
等等。
比如下面的 if_changed 的用法:
?
如果目标的依赖或者命令参数已经改变,那么将执行 cmd_vmlinux__ 所代表的新命令。那么这里可能会想到,在新老命令的比较中,老命令是从何而来?正如上面所分析的,老命令正是被写入到 $(dot-target).cmd 文件中的;这样一来,在 Makefile 中肯定有一处是将这些保存了老命令的文件包含进来的语句。在顶层 Makefile
中的 1481-1489 行可以看到:
?
在目录 scripts/ 下,像在 Makefile.build、Makefile.headersinst、Makefile.modpost 以及Makefile.fwinst 等文件中,也可以看到类似的读入语句。但这并不冲突,因为顶层 Makefile 和它们独立运作,变量间互不干扰。
if_changed_rule/cc_o_c/any-prereq/arg-check
Makefile 对应内核版本:2.6.35.13在 scripts/Makefile.build 的 86 行看到 builtin-target 的定义:
builtin -target := $(obj) /built-in .o |
$(builtin-target) : $(obj-y) FORCE $( call if_changed,link_o_target) |
kbuild- file := $( if $( wildcard $(kbuild-dir) /Kbuild ), $(kbuild-dir) /Kbuild , $(kbuild-dir) /Makefile ) include $(kbuild-file) |
obj-y:= main.o version.o mounts.o |
$(obj) /%.o: $(src) /%.c FORCE $( call cmd,force_checksrc) $( call if_changed_rule,cc_o_c) |
built-in.o 文件。
这里先分析 if_changed_rule 和 cc_o_c 的实现:
if_changed_rule 定义在 scripts/Kbuild.include 的 220-222 行中:
if_changed_rule = $( if $( strip $(any-prereq) $(arg-check) ),\ @ set -e;\ $(rule_ $(1) )) |
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 中,首先使用 $(filter-out $(PHONY),$?) 将 $? 中的所有伪目标去掉,不然可能会将 FORCE 这种目标也带进来,如果此时返回非空,那么说明有比目标还要新的依赖文件。$(wildcard
$^) 匹配当前目录下的所有依赖文件(已经存在的),然后再使用 $(filter-out $(PHONY) $(wildcard $^),$^) 将伪目标以及当前目录下匹配的文件列表从整个 $^ 列表中删除,如果返回不为空,那么说明某些依赖文件不存在,也就是说这些不存在的依赖文件还没生成 -- 这是因为某些依赖文件需要在编译时才会生成。
在 arg-check 中,$(1) 表示第 1 个参数,比如上面的 $(call if_changed_rule,cc_o_c) 中,$(1) 就是 cc_o_c ,所以 $(cmd_$(1) 就是表示 cmd_cc_o_c -- 这个变量可以在 Makefile.build 中的 179 或 194 行看到定义。它实际上表示的是这一次编译文件时所用到的命令行参数。
$@ 表示目标文件,从上面叙述可知,它就是 $(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) 。
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) );\ echo 'cmd_$@ := $(make-cmd)' > $(dot-target) .cmd) |
set -e 表示如果命令执行有错那么命令停止执行并退出。
接着 $(echo-cmd) 用来打印出相关的编译命令,接着执行 $(cmd_$(1) 里的命令。
最后 echo 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd 将上面执行的命令写入一个叫 $(dot-target).cmd 的文件中,该文件为隐藏文件,在编译后的内核源码目录及其子目录下随处可见,比如在 init/ 下可以看到 .initramfs.o.cmd, .version.o.cmd
等等。
比如下面的 if_changed 的用法:
?
.tmp_vmlinux2: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms1.o FORCE $( call if_changed,vmlinux__) |
中的 1481-1489 行可以看到:
?
1 2 3 4 5 6 7 8 9 | # 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 |
相关文章推荐
- Linux 编程学习笔记----ANSI C 文件I/O管理
- Linux 日常管理
- Linux学习笔记(二) - sort,uniq,cut,wc 命令详解
- 安装完CentOS 7 后必做的七件事
- 安装完CentOS 7 后必做的七件事
- 安装完CentOS 7 后必做的七件事
- 安装完CentOS 7 后必做的七件事
- 安装完CentOS 7 后必做的七件事
- linux中断申请之request_threaded_irq【转】
- linux下vi命令大全
- Centos 6.6 安装python3.4及Django1.9
- 驱动 ENC28J60 SPI网卡移植 linux 2.6.28 SAMSUNG 6410 ARM11
- linux 在vi编辑器里面有一个查找某个单词
- 梦想总是要有的—兄弟连IT教育
- Ubuntu text mode boot
- CentOS_6.5_X86 配置多网卡汇聚(bonding)
- linux系统下mySQL数据库 备份方法和脚本
- centos下gitlab安装过程和问题
- 从零开始 CentOs 7 搭建论坛BBS Discuz_X3.2
- linux 权限rwxrwxrwx(4 2 1)详细说明