Makefile三要素:
目标、依赖、命令
详解可见makefile 编写_周北-CSDN博客_makefile 编写
Makefile中常用函数和自动化变量:
wildcard-扩展通配符
例:OBJECTS=$(wildcard *.o)
该找到目标文件下的所有后缀为.c的文件名并将它们全都赋给OBJECT(即,OBJECT是.o文件的集合,并且这个函数中的OBJECTS是个任意命名的变量,但是$(wildcard .o)是这个实现找出.o文件的Makefile函数),要注意的是在wildcard和.o之间是是通过空格隔开的)。
patsubst-替换通配符
obj=$(patsubst %, $(LIBPATH)/%/$(LIB_TYPE), $(LINKDIRS))
该函数的功能是在LINKDIRS(第三个参数的集合)中取出各个单元(库所在目录名), 将所有包含第一个参数的部分替换成第二个参数的部分。要注意的是在这个函数中patsubst 和%之间是没有逗号将它们两个隔开的,它们两个之间只有空格,%, $(LIBPATH)/%/$(LIB_TYPE), $(LINKDIRS)之间则使用逗号隔开。
foreach-循环函数
也可使用类似于Shell的for循环语句:
for name in $(FILES); do \
echo "$$name"; \
done
注意, 在makefile中的shell变量要用2个$号表示变量名称
其他函数可参见makefile中常用函数_.-CSDN博客_makefile常用函数
Makefile三个自动化变量:
$@ 规则中的目标集合,在模式规则中,如果有多个重复目标的话,“$@” 表示匹配模式中定义的目标集合
$% 当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空
$< 依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%”)定义的,那么“$<” 就是符合模式的一系列的文件集合,多次调用则向后顺移读取
$? 所有比目标新的依赖目标集合,以空格分开
$^ 所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个文件“$^” 会去除重复的依赖文件,值保留一份
$+ 和“$^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件
$* 这个变量表示目标模式中"%"及其之前的部分,如果目标是式为a.%.c,那么“$*”就是test/a.test
makefile允许命令行传参,通常用于指定宏编译
实例解析可见一点一点学写Makefile(4) - 编译时指定宏参数_qq849635649的博客-CSDN博客_make 指定宏
makefile可以使用自动化构建工具CMake生成
Makefile中常见预定义变量
AR 库文件维护程序的名称,默认值为ar,创建静态库.a
AS 汇编程序的名称,默认值为as
CC C编译器的名称,默认值为cc
CPP C预编译器的名称,默认值为$(CC) –E
CXX C++编译器的名称,默认值为g++
FC FORTRAN编译器的名称,默认值为f77
RM 文件删除程序的名称,默认值为rm –f
ARFLAGS 库文件维护程序的选项,无默认值
ASFLAGS 汇编程序的选项,无默认值
CFLAGS C编译器的选项,无默认值
CPPFLAGS C预编译的选项,无默认值
CXXFLAGS C++编译器的选项,无默认值
FFLAGS FORTRAN编译器的选项,无默认值
GCC编译选项CFLAGS参数
-c 用于把源码文件编译成.o对象文件,不进行链接过程
-o 用于连接生成可执行文件,在其后可以指定输出文件的名称
-g 用于在生成的目标可执行文件中,添加调试信息,可以使用GDB进行调试
-Idir 用于把新目录添加到include路径上,可以使用相对和绝对路径,“-I.”、“-I./include”、“-I/opt/include”
-Wall 生成常见的所有告警信息,且停止编译,具体是哪些告警信息,请参见CC手册,一般用这个足矣!
-w 关闭所有告警信息
-O 表示编译优化选项,其后可跟优化等级0\1\2\3,默认是0,不优化
-fPIC 用于生成位置无关的代码
-v (在标准错误)显示执行编译阶段的命令,同时显示编译器驱动程序,预处理器,编译器的版本号
GCC链接选项LDFLAGS参数
-llibrary 链接时在标准搜索目录中寻找库文件,搜索名为liblibrary.a 或 liblibrary.so
-Ldir 用于把新目录添加到库搜索路径上,可以使用相对和绝对路径,“-L.”、“-L./include”、“-L/opt/include”
-Wl,option 把选项 option 传递给连接器,如果 option 中含有逗号,就在逗号处分割成多个选项
-static 使用静态库链接生成目标文件,避免使用共享库,生成目标文件会比使用动态链接库大
另外,LDFLAGS告诉链接器从哪里寻找库文件,LIBS告诉链接器要链接哪些库文件。
接下来利用一个简单地实例进行说明,安装Flex编译一个文本识别的小demo。Flex的源程序(定义词法解析处理规则)需要经过Flex工具形成完成的词法解析程序,最后和Main程序链接到一起。
编写testMain.c
#include <stdio.h>
extern int yylex();
extern int chars;
extern int words;
extern int lines;
void main(int argc, char **argv)
{
yylex();
printf("line: %d, word: %d, char: %d\n", lines, words, chars);
}
编写testFlex.l
%{
int chars = 0;
int words = 0;
int lines = 0;
%}
%%
[a-zA-Z]+ { words++; chars += strlen(yytext); }
\n { chars++; lines++; }
. { chars++; }
%%
根据编译过程,编写一个直观的Makefile
ECHO = echo #将编译器选项定义为变量,提高makefile文件的可移植性
CC = gcc
FLEX = flex
target: start testDemo #make命令默认执行第一个target及其依赖项
@$(ECHO) compiling is finished!
start:
@$(ECHO) compiling......
testDemo: testMain.o testFlex.o
$(CC) -o testDemo testMain.o testFlex.o -lfl -Wall
testMian.o: testMain.c
$(CC) -c testMian.c
testFlex.o: testFlex.c
$(CC) -c testFlex.c
testFlex.c: testFlex.l
$(FLEX) -o testFlex.c testFlex.l
clean: #make clean执行非第一个target子程序的clean target
@$(ECHO) cleaning......
rm testFlex.c ./*.o testDemo
根据编译过程,编写一个使用自动化变量的Makefile
ECHO = echo #将编译器选项定义为变量,提高makefile文件的可移植性
CC = gcc
FLEX = flex
OBJECTS = testMain.o
OBJECTS += testFlex.o
TARGET = ./testDemo
CFLAGS = -Wall
LIBS = -lfl
TARGET: $(OBJECTS) #make命令默认执行第一个target及其依赖项
$(CC) -o $(TARGET) $(OBJECTS) $(LIBS) $(CFLAGS)
@$(ECHO) compiling is finished!
#.c.o : 一种旧格式写法
%.o : %.c
$(CC) -o $@ -c $<
#.l.c :
%.c : %.l
$(FLEX) -o $@ $<
clean: #make clean执行非第一个target子程序的clean target
@$(ECHO) cleaning......
rm $(OBJECTS) $(TARGET) -f
.PHONY : clean
在clean目标中需要配合使用伪目标声明,这个伪目标声明是指当当前路径下含有以clean命令的文件或者是目录的时候,则在使用命令make clean的时候,就会导致结果无法执行。
解决这种问题,只需要在makefile中加入一个伪目标声明即可,格式为 .PHONY.clean。
具体说明可见makefile中的all和.PHONY的作用-andyhzw-ChinaUnix博客
另外,关于Makefile特殊目标的详细说明可见Makefile的特殊目标 | 学步园