makefile是GUN执行make调用的程序,用来组织项目中的文件按照一定的规则被编译,其核心是规则
程序的编译过程
先来看看程序的编译过程
大体上分为四步:
- 预处理
- 编译,生成.s文件
- 汇编,生成.o文件,是二进制文件
- 链接,将多个.o文件链接在一起,生成可执行文件ELF文件
file命令查看文件格式:
makefile语法
Makefile的基本格式很简单:
目标 : 依赖1 依赖2 ...
[TAB]命令
执行命令的条件:
- 当目标文件不存在
- 或者某个依赖比目标文件新
makefile通配符
- %.o 表示某个.o文件
- *.o 表示所有的.o文件
- $@ 表示目标
- $< 表示第1个依赖文件
- $^ 表示所有依赖文件
makefile变量的两个特色
Makefile中变量分为两类,一类是即时变量,另一类是延时变量,这在GUN make中文手册中被翻译为Makefile变量的两个特色
A := xxx
- A的值即刻确定,在定义时即确定
B = xxx
- B的值使用到时才确定
:= 即时变量
= 延时变量
?= 延时变量, 如果是第1次定义才起效, 如果在前面该变量已定义则忽略这句
+= 附加, 它是即时变量还是延时变量取决于前面的定义
makefile函数
使用这些函数的目的是为了替代手动输入大量的.c或者.o文件组成的字符串(大项目中可能有成千上万个文件),因此这些函数的输出都是字符串
makefile所有的函数都有返回值,我们使用函数也是为了读取它的返回值
- 查找所有
查找当前目录下所有的.c文件
src = $(wildcard ./*.c)
返回一个字符串,如下:
- 按格式替换
替换当前目录下的.c为.o的形式,.c来源于上面的src变量,注意这里是一对一替换,因此是%而不是*
obj = $(patsubst ./%.c, ./%.o, $(src))
返回也是一个字符串,注意这只是修改了字符串中每个文件的后缀生成一个新的字符串,并不生成文件,结果如下:
- 替换所有
将obj里面的文件依次取出并放在f中,并改成f.d的形式,注意f是变量,要加上$(f),输出这个字符串
oobj=$(foreach f, $(obj), $(f).d)
结果如下:
- 过滤文件
将src里面的符合xx.c形式的文件取出来,注意这里是依次取出,因此是%
obj=$(filter %.c, $(src))
输出结果:
- 反过滤函数
filter-out用法与filter相反,不再介绍
完整代码
这个代码会生成两个目标,适用于编译多个可执行文件,上面的输出信息写在终极目标的命令中,因为终极目标不需要执行任何gcc指令
.PHONY : clean,声明一个伪目标,在文件夹下有文件名为clean的文件时,依然会执行makefile里面的clean指令,否则会执行这个clean文件,做个试验测试一下即可
CC = gcc
CFLAGS = -Wall -g
# 查找,获取的也是一个字符串
src = $(wildcard ./*.c)
# 替换指定格式的文件
obj = $(patsubst ./%.c, ./%.o, $(src))
# 替换所有
dbj=$(foreach f, $(obj), $(f).d)
# 过滤文件
fbj=$(filter %.c, $(src))
target : client server
echo $(fbj)
client : client.o sckutil.o
$(CC) $(CFLAGS) -o $@ $^
server : server.o sckutil.o
$(CC) $(CFLAGS) -o $@ $^
%.o:%.c
$(CC) -c -o $@ $<
# 伪目标
.PHONY : clean
clean:
rm -rf *.o client server
以上是学习makefile必备的基础知识,知识来源于积累,,,
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!