文件搜寻
当依赖或者目标和Makefile不在同一个目录时,可通过VPATH(全部字母大写)来制定目标和依赖所在的目录。
make先查找当前目录,当前目录查找不到才会去VPATH制定的目录下查找。
VPATH = /root/headers:../include:/usr/include #多个目录之间通过":"分开
VPATH是一个很泛的指定,所有当前目录下找不到的文件都会去VPATH指定的目录下查找。
更精细的指定,可通过vpath(全部字母小写)指定,用法如下:
可以出现多个vpath,根据先后顺序查找
添加查找规则:
vpath %.h ../include
vpath %.c ../src
vpath % ../common
删除查找规则
vpath %.h
清楚所有已经设置好的索引目录:
vpath
在使用目录索引时,最要在规则中使用自动化变量($@,$^,$<等),这样就能获取到从VPATH中,索引到文件的完整路径。
当依赖是一个-lNAME时,make会自动将-lNAME扩充成libNAKE.so
1:首先会在当前目录查找此动态库是否存在
2:如果当前目录没找到,则通过VPATH,vpath指定的目录查找
3:如果以上都未找到,则会去系统目录查找/lib,/usr/lib等目录查找
4:如果没有找到动态库。则make会去查找相应的静态库libNAME.a
5:如果都没有找到,则报错。
foo: foo.c -lcurses
gcc -o $@ $^
伪目标
1:通过.PHONY声明一个伪目标为clean,伪目标不是一个文件,而是一个标签。需要运行时只能通过make clean来指定clean这个目标执行。
.PHONY: clean
clean:
@-rm -rf *.o
2:当需要执行一个make而把多个不相互依赖的目标同时执行,则可以使用all这个伪目标。
all: prog1 prog2 prog3
.PHONY: all
prog1: a.c a.h
gcc -o $@ $^
prog2: c.c c.h
gcc -o $@ $^
prog3: c.c c.h
gcc -o $@ $^
3:伪目标也可作为依赖
.PHONY: clean clean1 clean2
clean: clean1 clean2
@-rm -rf *.a
clean1:
@-rm -rf *.o
clean2:
@-rm -rf *.tmp
4:伪目标用于并行和递归过程
方法1:
SUBDIRS = foo bar
subdirs:
@set -e; \ #set -e 设定当执行出现异常时,立即退出
for dir in $(SUBDIRS);do \
$(MAKE) -C $$dir; \
done
方法2:(
可指定执行顺序)
SUBDIRS = foo bar
.PHONY: subdirs $(
SUBDIRS)
subdirs: $(SUBDIRS)
$(subdirs):
$(MAKE) -C $@
foo: bar #此句话的含义是bar在foo之前先执行
强制目标
如果一个规则没有命令或者依赖,并且目标不是一个存在的文件名。
FORCE:; ,执行类似的规则,目标总会被更新,依赖这个目标的目标,也会同时被更新。
foo: foo.c FORCE
gcc -o $@ $^
FORCE:
以上的例子,foo每次执行make都会重新编译。
多目标
bigoutput littleoutput: text.g
command text.g -$(subst output,,$@) >$@
等价于:
bigoutput:text.g
commond text.g -big > bigoutput
littleputput:text.g
commond text.g -little > littleoutput
多规则
对于一个多规则的目标,重建此目标的命令只能出现在一个规则中(可以是多条命令)。
如果多个规则同时给出重建此目标的命令,make将使用最后一个规则中所定义的命令,同时提示错误信息。
静态模式
TARGET(目标集合):target-pattern(每个目标文件):prereq-patterns(每个目标文件对应的依赖)
COMMOND
Example:
objects = a.o b.o c.el
all:$(objects)
$(filter %.o, $(object)):%.o:%.c
gcc -c $(CFLAGS) $< -o $@
以上Makefile相当于:
通过filter函数过滤出所需要的.o文件。
a.o:a.c
gcc -c $(CFLAGS) $< -o $@
b.o:b.c
gcc -c $(CFLAGS) $< -o $@
注意:all:$(objects)这句话必须要的,否则只会执行第一个a.o:a.c的规则。静态规则可以指定需要执行的目标,比默认规则好用。
$*自动变量,获取目标的名字(不带后缀)
Example:
all: a.o b.o
a.o b.o:%.o:%.c
@echo $* "===>" $@
OutPut:
a ===> a.o
b ===> b.o
双冒号规则:
双冒号规则:
允许同一个文件 作为 多个规则的目标。但是都必须使用双冒号!!
Example:
a.o::foo.c
gcc -o $@ -c $<
a.o::bar.c
gcc -o $@ -c $<
以上两条在makefile中是合法的。如果是单冒号的话,makefile则会提示错误。
双冒号与单冒号的区别:
1:目标没有依赖时,双冒号不管目标是否存在,每次都会编译。单冒号的情况下,如果目标已存在,则不会再进行编译。
2:通过一个目标作为多个双冒号的规则时,
每个规则都会独立的,顺序执行。单冒号都是为了生成一个目标,此目标不依赖的规则不会执行。
3:一个目标的多个双冒号规则,其中一个依赖更新了,只会执行其中的一个规则。
自动生成依赖:
file a.c:
#include
#include "a.h"
int main(void)
{
printf("%s %d\n", __func__, __LINE__);
return 0;
}
使用gcc -M a.c,产生的依赖是
带有标准库的头文件的:
a.o: a.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-32.h \
/usr/lib/gcc/i686-redhat-linux/4.4.6/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/i686-redhat-linux/4.4.6/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h a.h
使用gcc -MM a.c,将标准库头文件不输出,只输出项目中出现的头文件:
a.o: a.c a.h
自动产生依赖,建议使用方法:
%.d:%.c
$(CC) -MM $(CFLAGS) $< > $@.$$$$;\
sed 's,\($*\)\.o[ :]*,\1.o $@:,g' <$@.$$$$ > $@;\
rm -rf $@.$$$$
命令第一行:生成目标文件的依赖
foo.o: foo.c foo.h
命令第二行:将foo.o替换成foo.o foo.d。
$*自动化变量(foo),\1是标签,表示前面括号中的内容,实则为foo。
将foo.o替换成foo.o foo.d。存入foo.d文件中。
foo.o foo.d: foo.c foo.h
命令第三行:将临时文件删除。
生成
foo.o foo.d: foo.c foo.h,为什么要讲foo.d加入到目标中呢??
回答:首先将foo.o:foo.c foo.h放入到foo.d中,因此又产生了一个文件foo.d,那foo.d的依赖和foo.o是一样的。所以将foo.d放入到依赖中,一开更新时foo.d和foo.o同时被更新。
通过include将.d文件引用进来
src_file = foo.c
sinclude $(src_file: .c = .d)
Example:
TARGET_1依赖OBJS_1,OBJS_1通过.d文件进行编译。
$(TARGET_1):$(OBJS_1)
$(CC) $(OBJS_1) $(LDFLAGS) -o $(TARGET_1)
sinclude $(OBJS_1:.o=.d)
%.d:%.c
$(CC) -MM $(CFLAGS) $< > $@.$$$$;\
sed 's,\($*\)\.o[ :]*,\1.o $@:,g' <$@.$$$$ > $@;\
rm -rf $@.$$$$
阅读(1718) | 评论(0) | 转发(0) |