所谓“牵一发而动全身”,事物之间是有着依赖关系的。如果其中某个事物发生了变化,那么与之相关联的事物就可能也需要更新状态。如果事物B依赖于A,那么如果A发生了变化,那么B很可能就需要更新状态,而不依赖于A的C事物则可能无需改变。改变事物的状态通常是需要耗费能量(或者说资源)的。了解事物之间的依赖关系的益处在于,当系统中的某个事物发生变化时,可以只对相关事物进行状态更新,从而节省资源。
Makefile 就其本质而言就是表述事物之间的依赖关系和相应的改变事物状态的方法。
一个系统内部的各种依赖关系可以简化为系统内两两事物之间的依赖关系的集合。这个两两事物之间的依赖关系在Makefile中称为“规则”(Rule)。每条规则由3部分组成:目标文件,依赖文件,对应操作。目标文件和依赖文件之间用冒号(:)分隔。对应操作写在接下来的行以Tab字符开始的行中。如下所示:
target: dependency_1 dependency_1 update_target_step_1 @update_target_step_2
上面例子中的 @ 号表示在执行时不显示对应行的命令内容。
需要指出的是,事物之间的依赖关系有时组成依赖路径。比如A依赖于B,B又依赖于C,那么A事实上也是依赖于C的。所有相关规则加起来构成了一棵依赖树。
Makefile 描述了事物的依赖关系。当通过 make 命令要得到某个目标时,make 命令根据Makefile 的描述检查与之相关的文件是否发生了变化(通常是通过文件最后修改时间来判断),并根据规则描述对目标做出更新。
Makefile 及 make 这种机制,其实在社会中也很常见。社会的运作某种程度上也是一种Makefile。
Makefile 通常用来管理程序构建。
特殊变量
隐含变量
隐含规则
隐含规则可以看作是对一类规则的抽象概括。比如编译一个项目中的每一个C源文件的动作很多时候都是相同的。我们没有必要为每一个C源文件都写一个规则,我们只需要写一个“抽象”的规则就好了。
%.o: %.c $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
后缀规则是一种古老定义隐含规则的方式,在新版本的make中使用模式规则作为对它的替代,模式规则相比后缀规则更加清晰明了。
.c.o: $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
在新版本的make中,推荐的方式是为每一个源文件产生一个描述其依赖关系的Makefile文件。对于一个源文件“NAME.c”,对应的这个Makefile文件为“NAME.d”。
%.d: %.c $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ sources = foo.c bar.c sinclude $(sources:.c=.d)
all: hello_world hello_world: cFile1.o cFile2.o $(CC) $(LDFLAGS) -o $@ $^ clean: #echo "clean ..." $(RM) *.o