if_changed_rule/cc_o_c/any-prereq/arg-check - lcw_202的专栏 - 博客频道 - CSDN.NET

# lcw_202的专栏

##

- 目录视图
- 摘要视图
- 订阅

新版论坛系列介绍之二——功能介绍篇 2012 SDCC门票社区团购火热开启！ 公告：CSDN博客频道博客搬家功能上线！
移动盛会强档来袭，参会套票全线秒杀！ 没有重量只有质量：iPad版《程序员杂志》应用上线 “我最喜欢的技术博客”获奖公布！

### if_changed_rule/cc_o_c/any-prereq/arg-check

分类： Linux kernelBuild 2011-08-04 19:43 64人阅读 评论(0) 收藏 举报
Makefile 对应内核版本：2.6.35.13
在 scripts/Makefile.build 的 86 行看到 builtin-target 的定义：

1 | `builtin``-target :=``$(obj)``/built-in``.o`

builtin-target 是一个 builtin-in.o 文件。在 297 行中看到：

1 2 | `$(builtin-target)``:``$(obj-y)``FORCE` `` `$(``call` `if_changed,link_o_target)`

由此可见 builtin-in.o 文件依赖于各个 obj-y 。obj-y 定义在要编译的子目录的 Makefile 中，这些 Makefile 文件在 Makefile.build 的 43-44 行做了包含：

1 2 | `kbuild-``file``:= $(``if``$(``wildcard``$(kbuild-dir)``/Kbuild``),``$(kbuild-dir)``/Kbuild``,``$(kbuild-dir)``/Makefile``)` `include``$(kbuild-file)`

比如在 init 目录下可以看到 obj-y 的一行定义：

1 | `obj-y := main.o version.o mounts.o`

上面的这些 .o 文件又由 Makefile.build 中的 229-231 这几行的规则编译而成：

1 2 3 | `$(obj)``/%.o:``$(src)``/%.c FORCE` `` `$(``call` `cmd,force_checksrc)` `` `$(``call` `if_changed_rule,cc_o_c)`

实际上就是将对应的 .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 行中：

1 2 3 | `if_changed_rule = $(``if``$(``strip``$(any-prereq)``$(arg-check)``), \` `` `@``set``-e; \` `` `$(rule_``$(1)``))`

这里看到 arg-check 和 any-prereq 两个变量，它们分别定义在 scripts/Kbuild.include 的 188 和 200 行中：

1 2 3 4 | `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_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 中：

?

1
2
3
4
5
6

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

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 的用法：
?

1 2 | `.tmp_vmlinux2:` `$(vmlinux-lds)` `$(vmlinux-all)` `.tmp_kallsyms1.o FORCE` `` `$(``call` `if_changed,vmlinux__)`

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

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

分享到：
- 上一篇： Makefile中的+/- 符号
- 下一篇： vmlinux-init, vmlinux-main
查看评论
暂无评论
您还没有登录,请[登录]或[注册]
* 以上用户言论只代表其个人观点，不代表CSDN网站的观点或立场

个人资料

lcw_202

- 访问：36113次
- 积分：1092分
- 排名：第6899名

- 原创：49篇
- 转载：158篇
- 译文：0篇
- 评论：9条

文章搜索

文章分类

- Git(5)
- Linux kernel VFS(5)
- Linux kernel 相关(26)
- Linux 内存管理(36)
- Linux 文件系统(5)
- Linux 进程管理(12)
- Linux 进程间通信(14)
- Linux Network(3)
- Linux kernelBuild(10)
- Linux 启动(3)
- Linux应用(10)
- C语言、算法与数据结构(14)
- GTK(1)
- SBwindows(2)
- Serial(4)
- Xlib(6)
- 开发学习计划(2)
- 网卡驱动(7)
- 要读的文章(3)
- 调试(13)
- 汇编(4)
- bash+vim(12)
- ftrace(3)

Linux kernel

- 云（存储），松（耦合）
- 道恒无名
- 大宝(sodme)的专栏
- 少帅的天空
- gliethttp
- thewayma
- ckhitler的个人空间
- 大坡3D软件开发
- jiangxinyu的专栏
- hyp Knowledge Base
- li

Adroid

- Android开发网
- 李先静的文章
- 小马哥的博客
- RichardYSteven的专栏
- Linux内核学习
- 葡萄树 On The Road — 一个会操作计算机的民工
- 涌泉闪闪@原创空间
- www.opensourceproject.org.cn The Linux Kernel Primer
- Gilbert Wang的专栏

汇编

- Paul Carter's Homepage
- 编译点滴
- 游手好弦 信步涂鸦
- Roxy的笔记本
- welfear的博客
- chinahhucai.cublog.cn
- 飞翔,嵌入式linux性能优化
- li_guotao blog
- 水木社区-驱动开发

文章存档

- 2012年04月(2)
- 2012年03月(6)
- 2012年02月(9)
- 2012年01月(1)
- 2011年11月(2)
- 2011年10月(1)
- 2011年09月(1)
- 2011年08月(24)
- 2011年07月(6)
- 2011年06月(1)
- 2011年05月(6)
- 2011年04月(2)
- 2011年03月(19)
- 2011年02月(3)
- 2011年01月(6)
- 2010年12月(18)
- 2010年11月(65)
- 2010年10月(21)
- 2010年07月(3)
- 2010年04月(1)
- 2010年03月(6)
- 2009年12月(3)
- 2009年11月(1)
展开

阅读排行

- ubuntu 10.04 添加自启动脚本(1421)
- Ubuntu 上Gitweb安装指南(1110)
- 巧用backtrace系列函数，在不具备gdb环境的Linux系统上大致定位段错误位置(980)
- 页描述符与物理地址(相关主题帖子总结)(910)
- 内核sem、wait_queue_head_t、timer和kernel_thread使用驱动范例(890)
- TLB与cache的深入分析(844)
- linux2.6.29 CFS调度详细分析(二)(616)
- ubuntu 10.04 U盘安装时，GRUB引导指令(524)
- linux 顶层Makefile执行的流程(524)
- 调研内核调用栈方便的工具 kmalloc-top(448)

推荐文章

最新评论

- zone_watermark_ok

  firo_google: 这个讲解的比较合理：http://www.unixresources.net/linux/clf/l...

- 页描述符与物理地址(相关主题帖子总结)

  dzygd78n: 不错，这个解除了我的疑惑了！

- files_struct

  xulai1001: 新版内核里面使用了rcu锁。按您的分析，也就是说fdt一开始是指向files_struct里面原始的...

- files_struct

  xulai1001: 很有用。新版内核的学习了！

- linux 顶层Makefile执行的流程

  yuanjie_123: 楼主谢谢您的分享,再此我有一些疑问想请教一下.$(CURDIR)/Makefile Makefile...

- zone_watermark_ok

  wukui1008: min >>= 1;这个地方请问是干嘛这样。。。

- git使用说明

  lcw_202: Cloning the Wine Git repositorygit clone git://sou...

- 修改最后一次提交

  liuguo09: 6. git修改前几次commit的说明修改最近一次提交：git commit --amend修改更...

- RS-232 维基百科，自由的百科全书

  匿名用户: 请问：用什么方法才能知道他们是：TXD RXD GND三根线，怎样识别，有的机顶盒RS232是排插，...

  公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告
  京 ICP 证 070598 号
  北京创新乐知信息技术有限公司 版权所有
  联系邮箱：webmaster@csdn.net
  Copyright © 1999-2012, CSDN.NET, All Rights Reserved