自动生成依赖

在分析u-boot的makefile时,发现了自动生成依赖的这个方法,今天分析了一
下。已知自动生成依赖的规则在顶层目录的rules.mk文件里面约定:
rules.mk:
---------
#########################################################################

_depend:$(obj).depend

$(obj).depend:$(src)Makefile $(TOPDIR)/config.mk $(SRCS)
@rm -f $@
@for f in $(SRCS); do /
g=`basename $$f | sed -e 's//(.*/)/./w//1.o/'`; /
$(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; /
done

#########################################################################
---------

在大多数子目录中,子目录的Makefile文件中都包含上述规则来自动生成依赖。我们以
u-boot/board/smdk2410 为例:
# cd /u-boot/board/smdk2410
[root@localhost smdk2410]# ls
config.mk flash.c lowlevel_init.S Makefile smdk2410.c u-boot.lds
[root@localhost smdk2410]# vim Makefile
Makefile:
---------
include $(TOPDIR)/config.mk

LIB = $(obj)lib$(BOARD).a

COBJS := smdk2410.o flash.o
SOBJS := lowlevel_init.o

SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
SOBJS := $(addprefix $(obj),$(SOBJS))

$(LIB): $(obj).depend $(OBJS) $(SOBJS)
$(AR) $(ARFLAGS) $@ $(OBJS) $(SOBJS)

clean:
rm -f $(SOBJS) $(OBJS)

distclean: clean
rm -f $(LIB) core *.bak .depend

#########################################################################

# defines $(obj).depend target
include $(SRCTREE)/rules.mk

sinclude $(obj).depend

#########################################################################
---------

已知:

$(obj)时间上为空,所以上面任何文件加上这个前缀实际上都没有用,表示在当前目录下面。
$(TOPDIR) == $(SRCTREE) 就是u-boot顶层目录。

现在开始看这个Makefile,可知其终极目标为 LIB即$(obj)lib$(BOARD).a即libsmdk2410.a
而$(LIB)分别依赖于.depend,$(OBJS),$(SOBJS)

当make按规则去生成$(LIB)时,它首先要去查看.depend文件,初次make的时候,.depend文件
还没有生成,所以make下一步就会查找.depend的生产规则并生成一个.depend文件。乍一看
Makefile好像没有.depend这个次级目标,但是别急,关键在于:

include $(SRCTREE)/rules.mk

这句相当于把rules.mk原方不动的展开,并插入在自己的位置上。

我们再回头看看rules.mk文件,可以发现该文件有.depend目标。
所以.../smdk2410目录下面的Makefile在make的驱动下,会按照rules.mk里面约定的规则生成
.depend文件,并把该文件放入.../smdk2410目录。

再次贴上rules.mk:
---------
$(obj).depend:$(src)Makefile $(TOPDIR)/config.mk $(SRCS)
@rm -f $@
@for f in $(SRCS); do /
g=`basename $$f | sed -e 's//(.*/)/./w//1.o/'`; /
$(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; /
done
---------

其中.depend的依赖$(src)Makefile,$(TOPDIR)/config.mk的作用对于研究自动生成依赖的
这个问题意义并不重要,只是导入$(HOST_CFLAGS),$(CPPFLAGS)这些变量(我暂时说服自己)。
关键看$(SRCS),在这个地方$(SRCS)代表着当前目录下的源文件,包括:
flash.c lowlevel_init.S smdk2410.c

.depend文件的生成规则为一个for循环,也就是找出上述各个源文件的依赖关系并放入
.depend文件。

需要注意的是,这个规则的语法确实太麻烦,我至今没有看清楚,不过不要紧,知道它是把上述
各个源文件的依赖关系取出放入到.depend文件就行。要想弄得更清楚的话,要好好看看sed
命令的用法,这里不讨论了。

生成的.depend 文件的内容如下(给出概略形式):

.depend
---------
flash.o: flash.c xxx.h xxx.h ...
smdk2410.o: smdk2410.c xxx.h xxx.h ...
lowlevel_init.o: lowlevel_init.s xxx.h xxx.h ...
---------

我们再回到.../smdk2410下面的Makefile,看终极目标$(LIB)的另外两个依赖:
$(OBJS),$(SOBJS) 也就是flash.o,smdk2410.o 和 lowlevel_init.o

似乎我们在.../smdk2410下面的Makefile里面看不到上述目标,但是不要急,看看该Makefile的
最后一行:sinclude $(obj).depend
也就是说把flash.o,smdk2410.o 和 lowlevel_init.o的依赖关系包含过去了.

通过上述分析,我们知道终极目标$(LIB)的所有依赖关系及次级目标都理清楚了,Make命令最终
可以递归的把$(LIB)生成。

之所以称为"自动生成依赖",我们可以看到:flash.o,smdk2410.o 和 lowlevel_init.o的依赖
关系都是make自己来完成的,不需要我们手动改写。即使我们的某个源文件的依赖关系发生了变
化,则.depend文件的时间戳会比某个源文件的时间戳早,则make会重新生成.depend文件!这样
就减轻了繁琐的人工劳动。

最后,我们再来一个超级简单的例子:
=========

[root@localhost maketest]# ls -a
. .. app.c Makefile print.c print.h rules.mk

app.c:
---------
int main()
{
PrintStr("Hello world !");
return 0;
}

---------

print.h:
---------
void PrintStr(char *);

---------

print.c:
---------
#include<stdio.h>
#include"print.h"
void PrintStr(char *s)
{
puts(s);
return;
}

---------

Makefile:
---------
OBJS := app.o print.o
SRCS := $(OBJS:.o=.c)

Tar: .depend $(OBJS)
gcc -o $@ $(OBJS)

clean:
rm -f $(OBJS)

include rules.mk
sinclude .depend
---------

rules.mk:
---------
.depend: $(SRCS)
@rm -f $@
@for f in $(SRCS); do /
g=`basename $$f | sed -e 's//(.*/)/./w//1.o/'`; /
$(CC) -MM -MQ $$g $$f >> $@ ; /
done
---------

make之后:
[root@localhost maketest]# ls -a
. .. app.c app.o .depend Makefile print.c print.h print.o rules.mk Tar

[root@localhost maketest]# cat .dependapp.o: app.cprint.o: print.c print.h

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结