PS:直接从WORD拷贝,未对格式进行仔细整理
5 宏(Macro)
在makefile中可以定义和使用宏。宏的定义的语法是:
define 宏名称
宏值
endef
宏的使用方法和变量的使用方法是一样的。另外我们可以使用ifdef预处理指令判断一个宏是否定义。
MKDIR = mkdir
CP =cp
USRBINDIR=c:/1/2
define installtarget
@echo Installing $@ in $(USRBINDIR) ... ;\
$(MKDIR) -m 7700 $(USRBINDIR) ;\
$(CP) $@ $(USRBINDIR)/ ;\
@echo ... done.
endef
circle: $(OBJ) $(LIB)
$(CC) $(LDFLAGS) -o $@ $^
ifdef installtarget
$(installtarget)
endif
在例15中,目标circle的规则的命令会先生成可执行程序circle,然后判断宏installtarget是否存在,如果存在,则将circle拷贝到变量USRBINDIR定义的目录中去。
在C语言中,我们知道在编译源代码前会进行预处理:基本的操作是分析预处理指令和宏,并展开成“真正意义上的C代码”。
Makefile的处理与此类似,在执行makefile之前,make会分析makefile中的预处理指令,宏和变量,并最终在内存中展开成最终的makefile脚本,然后执行它。变量和宏,前提我们都已经介绍过了,这里我们要介绍makefile中的预处理指令。
6.1 包含(include)
可以使用include预处理指令在一个makefile文件中包含另外一个makefile文件
例2 使用inlude预处理指令
default.mk文件:
CC := gcc
CFLAGS := -Wall –g –std=c99
LDFLAGS := -lm
circle.mk文件:
include default.mk
circle : circle.o circulararea.o
$(CC) $(LDFLGAS) –o $@ $^
%.o :%.c
$(CC) $(CFLAGS) –o $@ –c $<
在例16中,有两个makefle文件:default.mk和circle.mk。其中circle.mk使用include指令包含了default.mk文件。circle.mk文件最终展开的效果与例14是等同的。
include指令的存在使得makefiel脚本的共用和模块化成为可能。
可以使用include一次包含多个makefile脚本。也可以使用SHELL通配符*和?。例如:
include $(HMOEDINDIR)/myutils.mk $(SRCDIR)/*.mk
还可以使用-include预处理指令,它的效果与include预处理指令是一致的,唯一的区别是:使用-include时,如果找不到要包含的文件,make程序会忽略错误,继续执行。
6.2 条件(conditional)
ifdef和ifndef预处理指令,来判断变量或宏是否定义或未定义。需要注意的是,“没有定义的变量”和“值被定义成空的变量”含义是一样的。
例3 使用ifdef预处理指令
OBJ := circle.o
LIB :=-lm
ifdef SHAREDLIB
LIB += circulararea.so
else
OBJ += circulararea.o
endif
circle: $(OBJ) $(LIB)
$(CC) –o $@ $^
%.so : %.o
$(CC) –shared –o $@ $^
在本例中,如果SHAREDLIB被定义,则circle将依赖于一个共享链接库circulararea.so,否则则是依赖于circulararea.o
ifeq和ifneq预处理指令,用来测试某个条件是否成立。
使用方法如下:
ifeq ($MATCHLIB), /usr/lib/libm.so)
# do something here
#endif
或:
ifeq “$MATCHLIB)” “/usr/lib/libm.so”
# do something here
#endif
6.3 vpath预处理指令
CC := gcc
CFLAGS := -Wall –g –std=c99
LDFLAGS :=-lm
circle : circle.o circulararea.o
$(CC) $(LDFLGAS) –o $@ $^
%.o :%.c
$(CC) $(CFLAGS) –o $@ –c $<
在上面的makfile脚本中,会编译两个源文件:circle.c和circulararea.c。这里需要提出一个问题,那就是make会到那里去找这两个源文件?默认情况下,make会到当前目录下去寻找源代码文件。但是,如果源代码文件不在当前目录下呢?当然我们可以指定一个相对路径或绝对路径,如下:
CC := gcc
CFLAGS := -Wall –g –std=c99
LDFLAGS :=-lm
SRCDIR := project/src
circle : circle.o circulararea.o
$(CC) $(LDFLGAS) –o $@ $^
%.o :$(SRCDIR)/%.c
$(CC) $(CFLAGS) –o $@ –c $<
这样make就会到当前路径下的project/src目录下去寻找源文件。但是问题是往往一个项目会有多个目录存放源代码,用上面的方式很明显无法用一个模式规则,处理所有的源代码文件。
好在可以使用vpath预处理指令,它指定与某个特定模式匹配的一类文件名的查找目录。
例4 使用vpath预处理指令
CC := gcc
CFLAGS := -Wall –g –std=c99
LDFLAGS :=-lm
SRCDIR1 := project/src1
SRCDIR2 := project/src2
vpath %.c $( SRCDIR1) $( SRCDIR2)
circle : circle.o circulararea.o
$(CC) $(LDFLGAS) –o $@ $^
circle.o circulararea.o:%.o : %.c
$(CC) $(CFLAGS) –o $@ –c $<
在例18中,如果make在当前路径下找不到要处理的源代码文件,就会到project/src1和project/src2中去寻找。
当然,我们还可以使用vpath去处理其他类型的文件,例如:
vpath %.o $( OBJDIR1) $( OBJDIR2) ……
vpath %.h $( INCDIR1) $(INCDIR2) ……
我们还可以使用VPATH内置变量[LU1] 来设置前提文件的查找目录。它与vpath指令的区别是,vpath指令是指定特定类型的前提文件查找目录,而VPATH内置变量则指定了所有类型的前提文件的查找目录。
例5 使用VPATH内置变量
CC := gcc
CFLAGS := -Wall –g –std=c99
LDFLAGS :=-lm
SRCDIR1 := project/src1
SRCDIR2 := project/src2
OBJDIR := project/obj
VPATH= $(SRCDIR1): $(SRCDIR2): $(OBJDIR)
circle : circle.o circulararea.o
$(CC) $(LDFLGAS) –o $@ $^
circle.o circulararea.o:%.o : %.c
$(CC) $(CFLAGS) –o $@ –c $<
VPATH中指定的目录名之间用:分割。
在make中,还有一些其他的预处理指令,例如override,export,unexport,由于不经常使用,这里就不一一介绍了。
[LU1]关于内置变量,除了VPATH外,其他的个人感觉一般都很少会用到,本文档就不做介绍了,读者只需知道内置变量是makefile中固有的变量即可了。