在Makefile中,是支持函数使用的,Makefile中的函数包括make解释器自身预定义的函数,同时也支持我们自己定义函数。
在Makefile中, 通过define关键字来实现函数的自定义,并以endef关键字结束,自定义函数使用预定义函数call调用,后边跟自定义函数名及参数,如下就是一个简单的自定义函数:
.PHONY : test
define fun1
@echo "My name is $(0)"
endef
define fun2
@echo "My name is $(0), param is $(1)"
endef
test:
$(call fun1)
$(call fun2, hello Makefile)
上边我们首先定义了一个伪目标test,接着定义了两个函数,fun1和fun2。fun1中直接通过$(0)输出call 的第一个参数,也就是函数名fun1,fun2中同时输出了第二个参数(这看起来有点 类似C语言里的main函数参数)。 在调用自定义函数fun1和fun2时,格式为$(call 函数名,参数...),call函数的参数需要以逗号","隔开,下边 执行如下:
除了可以自定义函数外,make解释器还提供了众多预定的函数供我们使用,比方abspath(取文件的绝对路径)
.PHONY : test2
var := $(abspath ./)
test2 :
@echo $(var)
这里需要注意的是, 当我们使用make解释器中预定义的 函数时,不需要通过call调用,而是直接$(函数名 参数1,参数2,...),下边make一下:
可见,输出了我当前路径的绝对路径。
上述仅仅是一些基础知识,下边就来使用make中的变量与函数来实现一个小综合例子(会使用到一些预定义函数),要求如下:
1、自动生成target文件夹 存放可执行文件
2、自动生成objs文件夹存放编译生成的目标文件(*.o)
3、 支持调试版本的 编译选项
4、考虑代码的可扩展性
在开始之前,首先来了解几个关键技巧
1、自动获取当前目录下的源文件列表(预定义函数wildcard)
- SRCS := $(wildcard *.c)
2、由源文件列表 生成目标文件列表(变量的值变换)
-OBJS := $(SRCS : .c=.o)
3、为每个 目标文件列表增加路径前缀(预定义函数调用addprefix)
- OBJS := $(addprefix path/, $(OBJS))
4、规则的模式替换,这里一共可以分为两种形式:
- 变量中的规则模式替换,如下
其中$(OBJS):%.o:%.c的意思就是,$(OBJS):%.o在变量OBJS中 逐个匹配后缀为.o的文件,将匹配到的变量作为目标,然后再将该变量的后缀由.o替换为.c,作为目标的依赖。合成的 步骤就是$(OBJS):%.o:%.c -> func.o:%.c -> func.o:func.c,这样变形成了func.o为目标,func.c为依赖的一条规则,同理main.o与main.c的目标依赖关系也是 这么形成的。
- 目录中的规则模式替换,目录与变量的主要区别就是一个是从当前目录中去匹配,一个是从变量中去匹配。
这里我们可以看到目录中的规则模式匹配,省去的目标 变量$(OBJS),而是直接 %.o:%.c 从 当前目中去进行.o文件的模式匹配。
对于以上5点技巧,我们以一个简单的实例来展示:
.PHONY : all clean
CC := gcc
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
OBJSPATH := $(addprefix path/, $(OBJS))
all: $(OBJS)
@echo "SRCS => $(SRCS)"
@echo "OBJS => $(OBJS)"
@echo "OBJSPATH => $(OBJSPATH)"
#变量中的规则模式替换
#$(OBJS):%.o:%.c
# @echo $(CC) -o $@ -c $^
#目录结构的规则模式替换
%.o:%.c
@echo $(CC) -o $@ -c $^
clean:
$(RM) -f *.o
make的结果如下,可以看到,我们使用的这些技巧,能可以方便的帮助我们编写易于维护的Makefile,当项目工程越来越大时,这就显得尤为重要:
有了以上的知识我们开始编写综合实例,当前目录下有如下文件:
Makefile文件如下:
CC := gcc
MKDIR := mkdir
RM := rm -rf
DIR_OBJS := objs
DIR_TARGET := target
DIRS := $(DIR_OBJS) $(DIR_TARGET)
TARGET := $(DIR_TARGET)/main.out
# main.c fun.c
SRCS := $(wildcard *.c)
# main.o fun.o
OBJS := $(SRCS:.c=.o)
#./objs/main.o ./objs/fun.o
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
.PHONY : rebuild all clean
$(TARGET) : $(DIRS) $(OBJS)
$(CC) -o $@ $(OBJS)
@echo "target file => $@"
$(DIRS):
$(MKDIR) $@
#debug与release版本编译选择
#目录结构的规则模式替换,因为.o文件在objs目录下,所以要加objs路径
$(DIR_OBJS)/%.o:%.c
ifeq ($(DEBUG),true)
$(CC) -o $@ -g -c $^
else
$(CC) -o $@ -c $^
endif
rebuild : clean all
all : $(TARGET)
clean:
$(RM) $(DIRS)
下边分别来编译debug版本与release版本,编译debug版本时在命令行后加DEBUG := true即可
编译成功后的目录结构如下
可以看到,多出了objs与target两个文件夹,里边的内容分别为.o目标文件与.out可执行文件
到此,我们的一个小小的可维护的Makefile综合实例已经完成,更多的内容待后续慢慢完善。