来写Makefile吧
一些小程序可能使用Makefile就足够了,比如只有几个源文件的C++程序,使用CMake可能有点杀鸡用牛刀,直接g++又会重复输入,所以写个Makefile比较省心
命名:makefile
Makefile
Makefile书写规则
目标...:依赖...
命令(shell命令)
....
....
#目标是最终要生成的文件,依赖是生成目标所需的文件或目标,命令前必须Tab缩进
demo1:sub.c div.c add.c main.c mult.c
gcc sub.c add.c div.c main.c mult.c -o demo1
root@ziggy-virtual-machine:~/learn_Makefile# ./demo1
a = 20, b = 12
a + b = 32
a - b = 8
a * b = 240
a / b = 1.666667
Makefile工作原理
一个Makefile文件中可以有多条规则
在执行规则之前,会先检查依赖项是否存在
如果依赖项不存在且前面的规则使用了依赖项,则会向下查找其他规则
demo1:sub.o div.o add.o main.o mult.o
gcc sub.o add.o div.o main.o mult.o -o demo1
sub.o:sub.c
gcc -c sub.c -o sub.o
add.o:add.c
gcc -c add.c -o add.o
mult.o:mult.c
gcc -c mult.c -o mult.o
main.o:main.c
gcc -c main.c -o main.o
div.o:div.c
gcc -c div.c -o div.o
root@ziggy-virtual-machine:~/learn_Makefile# make
gcc -c sub.c -o sub.o
gcc -c div.c -o div.o
gcc -c add.c -o add.o
gcc -c main.c -o main.o
gcc -c mult.c -o mult.o
gcc sub.o add.o div.o main.o mult.o -o demo1
我们的规则书写一般都是为第一条规则来服务(Makefile自身默认执行第一条规则,如果下面的规则和第一条没啥关系,则不会执行)
Makefile会检测依赖和目标文件的更新
检测目标文件和依赖的新旧程度,如果依赖比目标文件早,则make不会重新执行内容
root@ziggy-virtual-machine:~/learn_Makefile# make
make: 'demo1' is up to date.
这样,我们写的第二个版本的Makefile更好,因为如果单独修改了某几个依赖,它不会将所有的依赖都重新编译
更完善的Makefile
用自定义变量和预定义变量简化Makefile
一大堆规则,过于繁琐也不好修改
幸亏Makefile有自定义变量:变量名:变量值
预定义变量
AR:默认值为ar 使用ar rc生成静态库文件
CC:默认cc
CXX:默认g++
$@:目标的完整名称
$<:第一个依赖文件的名字
$^:所有有依赖文件
app:main.c a.c b.c
gcc -c main.c a.c b.c
上面的Makefile可以替换为:
app:main.c a.c b.c
$(CC) -c $^ -o $@
在shell命令中的make选型也可以:make CC=arm-linux-gcc
这样会将Makefile中所有CC变量的默认值cc替换为arm-linux-gcc
#自定义变量
src=sub.o div.o add.o main.o mult.o
target=demo2
$(target):$(src)
$(CC) $(src) -o $(target)
sub.o:sub.c
gcc -c sub.c -o sub.o
add.o:add.c
gcc -c add.c -o add.o
mult.o:mult.c
gcc -c mult.c -o mult.o
main.o:main.c
gcc -c main.c -o main.o
div.o:div.c
gcc -c div.c -o div.o
使用通配符
%
src=sub.o div.o add.o main.o mult.o
target=demo3
$(target):$(src)
$(CC) $(src) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
上面Makefile中,每个依赖文件都会去匹配第二条规则
自动获取依赖文件列表
$(wildcard [file_pattern])
#[]中的是某个或多个目录下对应的某种类型的文件
#使用例子:
$(wildcard *.c /root/newc/*.c)
#返回值为:xx.c xxx.c x.c
#由于我们需要的是.o文件名,所以需要将.c文件名批量替换,使用:
$(patsubst %.c, %.o, $(src))
#格式:$(patsubst <file_pattern>, <replace>, <text>)
作用为查找text种的单词(以空格,Tab或回车分隔)如果匹配file_pattern的格式,则以replace格式替换
此函数返回被替换后的字符串,即为我们需要的依赖文件名列表
如果前两者都有%,则replace中的%将是pattern中%所代表的字符串
%可以用'\'
转义,来表示真实的%
src=$(wildcard *.c)
srco=$(patsubst %.c, %.o, $(src))
target=demo5
$(target):$(srco)
$(CC) $(srco) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
src=$(wildcard *.c)
srco=$(patsubst %.c, %.o, $(src))
target=demo5
$(target):$(srco)
$(CC) $(srco) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
clean:
rm $(srco) -f
make clean
src=$(wildcard *.c)
srco=$(patsubst %.c, %.o, $(src))
target=demo5
$(target):$(srco)
$(CC) $(srco) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
#clean是没有依赖文件的,为了防止目录中有clean文件,所以我们使用伪目标
.PHONY:clean
clean:
rm $(srco) -f