在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综合实例已经完成,更多的内容待后续慢慢完善。
| | | |--|--| | | |