makefile初探
1、Makefile语法规则:
-
一条规则:
目标:依赖文件列表 <tab>命令列表
-
Makefile基本三要素:
- 目标:通常是要产生的文件名称,可以是可执行文件或其他对象文件,也可以是动作名称
- 依赖文件:1)用来输入从而产生目标的文件;2)一个目标通常有几个依赖文件(也可以没有)
- 命令:1)make执行的动作,一个规则可以包含多个命令,每个命令占一行。
-
Makefile规则:
- make命令会在当前目录下找到名为Makefile或makefile的文件
- 与make命令执行相关联的是系统维护的文件时间戳,当依赖文件(prerequisties)的时间戳比目标文件(target)要新时,make会执行target下的命令重新生成target,详细参考这里(Makefile Tutorial By Example)
- 如果关联的依赖文件不存在,那么会继续寻找目标为依赖文件的规则,找到后执行对应的命令生成所需依赖文件,最后完成目标文件生成。
2、make命令格式:
- make [-f file] [options] [targets]
- -f 指定文件作为makefile输入文件。
- [options]:
- -n : 只打印要执行的命令但不执行。
- -s : 执行但不显示执行的命令。
- [targets]:
- 如果make如果没有指定目标,make默认实现makefile文件内第一个目标,然后退出。
- 指定了make要实现的目标,目标可以是一个或多个,多个目标间用空格隔开。
3、Makefile变量:
-
变量定义:
- 变量名=变量值 或者 变量名:=变量值
-
cpp := main.cpp obj = main.o
-
变量引用:
-
在使用变量时,需要在变量名前加 $ 并用()或{}包裹起来。
-
cpp := main.cpp obj = main.o $(obj) : ${cpp} g++ -c $(cpp) -o $(obj) # 等价于: g++ -c main.cpp -o main.o complie : $(obj)
-
makefile的变量名:
-
变量名区分大小写
-
变量一般在makefile的头部定义。
-
makefile提供的一些变量供用户直接使用,可以直接对其赋值。
-
CC=gcc CPPFLAGS:C预处理选项:-E CFLAGS:c编译器选项 -Wall -g -c LDFLAGS:链接器选项 -L -l
-
-
示例:
-
假设写了加减乘除四个函数分别写在 add.c sub.c mul.c div.c 和一个调用四则运算的程序test.c则使用make编译如下:
-
首先是makefile文件:
-
TARGET:=test # 定义编译完生成的可执行文件名为 test OBJS=add.o sub.o mul.o div.o test.o # SOURCE=add.c sub.c mul.c div.c test.c $(TARGET):$(OBJS) gcc $(OBJS) -o $(TARGET) #错误,gcc -c add.c sub.c mul.c div.c test.c -o add.o sub.o mul.o div.o test.o只生成了add.o一个文件,后面的文件都没有生成,所以要分开写。 # $(OBJS):$(SOURCE) # gcc -c $(SOURCE) -o $(OBJS) add.o:add.c gcc -c add.c -o add.o sub.o:sub.c gcc -c sub.c -o sub.o mul.o:mul.c gcc -c mul.c -o mul.o div.o:div.c gcc -c div.c -o div.o test.o:test.c gcc -c test.c -o test.o clean: rm -rf $(OBJS)
-
第一次出错的结果:
-
分开写后运行成果的结果:
-
执行编译完的可执行文件test的结果:
-
修改test.c文件后,再次执行make命令结果为:
- 可以看到,只改变了test.c文件后执行make命令,只有test.o和 TARGET 两个目标的命令重新执行
-
重新执行test可执行文件后:
-
-
4、自动变量与模式规则:
-
上面的写法太过复杂,自动变量与模式规则可以替我们简化写法:
-
自动变量:
-
$@ : 表示目标(target)的完整名称。
-
$< : 第一个依赖文件的名称。
-
$^ : 所有的依赖文件,以空格分开,不包含重复的依赖文件
-
注意: 自动变量只能在规则的命令中使用。
-
示例:
-
TARGET:=test OBJS=add.o sub.o mul.o div.o test.o CC=gcc $(TARGET):$(OBJS) # gcc $(OBJS) -o $(TARGET) $(CC) $^ -o $@ #使用自动变量改写上面的写法 echo $@ # 打印目标 echo $^ # 打印所有依赖 echo $< # 打印第一个依赖 add.o:add.c # gcc -c add.c -o add.o $(CC) -c $< -o $@ # 等价于 $(CC) -c $^ -o $@ sub.o:sub.c # gcc -c sub.c -o sub.o $(CC) -c $< -o $@ mul.o:mul.c # gcc -c mul.c -o mul.o $(CC) -c $< -o $@ div.o:div.c # gcc -c div.c -o div.o $(CC) -c $< -o $@ test.o:test.c # gcc -c test.c -o test.o $(CC) -c $< -o $@ clean: rm -rf $(OBJS)
-
-
结果:
-
上面的各个子依赖的部分执行的命令都很相似,都是将源文件编译为二进制文件(*.o)。所以makefile又引入模式规则。
-
-
模式规则:
-
模式规则示例:
-
*与%通配符:
- *: 通配符表示匹配任意字符串,可以用在目录名或文件名中。
- %: 通配符表示匹配任意字符串,并将匹配到的字符串作为变量使用。
TARGET:=test OBJS=add.o sub.o mul.o div.o test.o CC=gcc $(TARGET):$(OBJS) $(CC) $^ -o $@ %.o:%.c # 通配符的使用,%后可以代表多个字符 $(CC) -c $< -o $@
-
结果:
-
5、Makefile中的函数:
-
wildcard 函数:查找指定目录下的指定类型的文件。
-
src=$(wildcard *.c) #找到当前目录下所有后缀名为.c的文件,赋值给src,wildcard后需要空一格
-
-
patsubst 函数:匹配替换
-
obj=$(patsubst %c,%.o,$(src)) #把src变量里面所有后缀为.c的文件替换成.o
-
-
makefile中所有函数都是由返回类型的。
-
SRC=$(wildcard *.c) # 查找当前目录内所有后缀为.c的文件存在SRC中 OBJS=$(patsubst %.c,%.o,$(SRC)) # 将SRC变量里所有后缀为.c的文件替换为.o文件 TARGET=test CC=gcc $(TARGET):$(OBJS) $(CC) $^ -o $@ %.o:%.c $(CC) -c $< -o $@ clean: rm -rf $(OBJS)
-
结果示例:
-
-
makefile的为目标:
-
当目标比如clean的当前目录下有同名的clean文件,则不执行clean对应的命令,需要make下的为目标。
-
伪目标声明:.PHONY:clean声明为目标后,makefile就不会判断目标是否存在或是否需要更新而直接执行目标。
-
clean命令中的特殊符号:
- “-”此条命令出错,make也会继续执行后续命令。
- “@”不显示命令本身,只显示结果。
-
SRC=$(wildcard *.c) # 查找当前目录内所有后缀为.c的文件存在SRC中 OBJS=$(patsubst %.c,%.o,$(SRC)) # 将SRC变量里所有后缀为.c的文件替换为.o文件 TARGET=test CC=gcc $(TARGET):$(OBJS) $(CC) $^ -o $@ %.o:%.c $(CC) -c $< -o $@ .PHONY:clean # 设置伪目标,不管clean是否存在或是否更新,都会执行 clean: rm -rf $(OBJS)
-
-
带有路径的Makefile文件编写:
源文件结构:
要求:生成的目标文件存储在obj目录中, 生成的可执行文件test存储在bin目录中。
# 获取src目录下所有的.cpp文件
SRC=$(wildcard ./src/*.cpp)
#替换为 obj/*.c
OBJS=$(patsubst ./src/%.cpp, ./obj/%.o,$(SRC))
CC=g++
TARGET=./bin/main
$(TARGET):$(OBJS)
$(CC) $^ -o $@
./obj/%.o:./src/%.cpp
$(CC) -c $< -o $@
run:
$(TARGET)
.PHONY:clean
clean:
rm -rf %(OBJS) $(TARGET)
all:
echo $(SRC)
echo $(OBJS)