makefile代码包含两部分:依赖关系、生成目标的方法
hw.o : hw.c def.h #说明依赖关系
cc -c -g hw.c #如何生成目标的方法
语法
targets : prerequisites
command #Tab开头
targets : prerequisites ; command #分号分割命令(过长命令,可以用"\"作为换行符)
通配符
make 支持的通配符:“*”、“?”、“[…]”
~符号在文件名中有特殊的用途,一般用来表示$HOME目录
“$?”是一个自动化变量
rm -f *.o #表示所有后缀为o的文件
objects = *.o #变量中使用通配符
objects := $(wildcard *.o) #关键字指示展开
文件搜寻
当有大量的源文件时,通常是把这些源文件进行分类,并存放在不同的目录中。makefile中的特殊变量 VPATH 可以让make到指定的目录中去找文件
VPATH = src:../include #冒号分割目录,依次搜索,当前目录最高优先搜索
#关键字vpath的方法:它可以指定不同的文件在不同的搜索目录中
vpath <pattern> <directories> #为符合模式<pattern>的文件指定搜索目录<directories>
vpath <pattern> #清除符合模式<pattern>的文件的搜索目录
vpath #清除所有已被设置好了的文件搜索目录
#<pattern>指定了要搜索的文件集,而<directories>则指定了<pattern>的文件集的搜索的目录
vpath %.h ../include #<pattern>需要包含“%”字符以匹配零或若干字符
#连续使用vpath语句,按先后顺序执行搜索,即便有相同或重复的<pattern>
vpath %.c e:g
vpath % f
vpath %.c g
伪目标
“伪目标”并不是一个文件,而只是一个标签,make 无法生成它的依赖关系和决定它是否要执行,只有通过显示地指明这个“目标”才能让其生效。
为了避免和文件重名的这种情况,可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”来说明不管是否有这个文件,这个目标就是“伪目标”。
.PHONY: clean
clean:
rm *.o
伪目标一般没有依赖的文件,但也可以为伪目标指定所依赖的文件。
伪目标同样可以作为“默认目标”,只要将其放在第一个。
# 一次生成若干个可执行文件(目标文件在同一个makefile中)
all : p1 p2 p3 #伪目标的特性是总是被执行,所以其依赖的目标总是会被决议
.PHONY : all
p1 : p1.o utils.o
cc -o p1 p1.o utils.o
p2 : p2.o
cc -o p2 p2.o
p3 : p3.o common.o
cc -o p3 p3.o common.o
目标可以成为依赖,伪目标同样也可以成为依赖
#可以通过输入不同的命令达到清除不同种类文件的目录
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
多目标
“$@”自动化变量,表示规则中的目标集
files = f.c b.o l.o
$(filter %.o,$(files)): %.o: %.c #$表示执行函数filter
$(CC) -c $(CFLAGS) $< -o $@ #“$<”表示示所有的依赖目标集,“$@”表示目标集
静态模式
静态模式可以更加容易地定义多目标的规则,让规则变得更加富有弹性和 灵活性
#targets 定义了一系列的目标文件,可以有通配符,是目标的一个集合
#target-parrtern 是指明了targets的模式,也就是的目标集模式
#prereq-parrterns 是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
objects = f.o b.o
all: $(objects)
$(objects): %.o: %.c #集合模式 依赖模式, %通配符取集合的前缀
$(CC) -c $(CFLAGS) $< -o $@ #自动化变量“$<”表示示所有的依赖目标集,“$@”表示目标集
自动生成依赖关系
大多数的 C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。
cc -M main.c
gcc -MM main.c #GNU的C/C++编译器, -M会将标准库头文件也包含
GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“.c”的文件都生成一个“.d”的 makefile 文件,[.d]文件中就存放对应[.c]文件的依赖关系,可以让 make 自动更新或自动生成[.d]文件,并把其包含到主 makefile 中,这样就可以自动化地生成每个文件的依赖关系了。
#产生[.d]文件的模式规则
%.d: %.c #依赖
@set -e
rm -f $@ #删除所有目标集
$(CC) -M $(CPPFLAGS) $< > $@.$$$$ #为依赖集生成目标集临时文件,“$$$$”表示一个随机编号
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@ #替换
rm -f $@.$$$$ #删除目标集临时文件
#目的是将.d文件也作为目标,自动更新,自动生成,[.d]文件中加入也不只有依赖关系,生成的命令也可加入,让每个[.d]文件都包含一个完赖的规则
把这些自动生成的规则include到我们的主makefile中,
sources = f.c b.c
include $(sources:.c=.d) #把变量sources中所有.c都替换成.d
#include是依次载入文件,最先载入的[.d]文件中的目标会成为默认目标,所以要注意次序