makefile入门
库文件
目标文件的集合(*.o)
静态库和动态库
静态库:类比家用车,将库打包到产品(可执行程序)
动态库:类比F1赛车,运行时才加载到可执行程序
可执行程序大小 | 部署难度 | 升级难度 | |
---|---|---|---|
静态库 | 大 | 易 | 难 |
动态库 | 小 | 难 | 易 |
静态库的生成
- 生成目标文件
- 把目标文件打包成静态库
- 将静态库移动到/usr/lib目录下
- 链接时需要加上静态库的名字
动态库的生成
1、生成目标文件(加上-fpic)
2、把目标文件打包成动态库
3、将动态库移动到/usr/lib目录下
4、链接时加上动态库的名称
Makefile文件
管理文件之间的依赖关系
Makefile文件的书写逻辑是这样的:首先,先确定需要生成的目标文件,然后,根据目标文件确定 它所需的依赖文件,此后,递归地找到依赖文件的依赖文件,直到依赖文件是没有子依赖文件(例 如,.c文件,.h文件等等)。
[target]:[prerequisites]
<tab>[command]
-
增量编译:修改了那个文件,就只需要编译那个文件
-
Makefile:规则的集合
- 目标:要生成的文件
- 依赖:目标文件由哪些文件生成
- 命令:通过执行命令由依赖文件生成目标文件,注意每条命令之前必须有一个tab保持缩进,这是语法要求
-
什么时候执行命令?
- 目标不存在
- 依赖比目标新
Makefile会自动根据文件的修改时间来判断是否执行指令。如果目标比所有的依赖文件都要“新”, 那么就不会执行有关这个目标的所有指令,这个规则对于依赖文件也生效,如果修改了某个原始代 码文件,make命令只会根据修改时间,来调整有影响的文件。
-
格式:make [target]
- 如果省略目标,默认执行第一个目标
对下图Makefile文件的分析:初始目标是main,首先需要得到依赖文件main.o,add.o,sub.o,mul.o和div.o,依赖文件又分别依赖于.c文件,然后利用gcc -c命令得到.o依赖文件,最后再执行gcc -o命令得到main可执行程序。
make的原理
DAG(有向无环图):表示文件之间的依赖
拓扑排序:任务调度(图的深度优先遍历)
Makefile进阶
伪目标 .PHONY
- 有些时候,使用make时并不希望得到最开始的目标文件,而是中间的目标文件。在make命令以 后添加目标文件的名字就能完成需求。
make [target]
-
例如使用make main.o可以只生成main.o这个目标文件,而不会执行前面的命令
-
利用上述特点,可以专门设置一些伪目标(.PHONY),伪目标并不是生成程序所必须的可执行文 件或者依赖文件,它们更加类似于实现其他功能的命令,例如清理二进制文件,重新生成代码等等.
.PHONY:clean rebuild
rebuild:clean main
clean:
rm -rf main.o file.o main
- 伪目标设计的主要是为了避免中间依赖文件和clean、rebuild重名的情况(这种情况,make命令 会认为clean已经存在,就不再需要修改的情况),执行伪目标的用法和一般目标一样
make clean
make rebuild
变量
自定义变量
- Makefile可以定义变量,在调用的时候,需要使用$()来引用变量(实际上就是字符串替换)
out = main #out代表了main,在运行的时候会进行字符串替代
$(out):main.o func.o
gcc -o $(out) main.o func.o
- 因为 = 定义变量会在执行的时候出现字符串替代,所以出现递归定义的时候,会进行递归展开。 但是有些情况,我们不希望递归展开,只希望进行一次字符串替换,这种情况可以采用 := 来定义 变量,这也是工作当中的主流用法
out := main #out代表了main,在定义完成的时候会进行字符串替代
$(out):main.o func.o
gcc -o $(out) main.o func.o
预定义变量
- 预定义变量就是内部定义好的变量,这些变量的含义是固定的
自动变量
- 自动变量:随着规则不同,自动变量具有不同的值
Makefile改写:
OBJS:=main.o func.o
CC:=gcc
main:$(OBJS)
$(CC) -o $@ $^ #$@代表main $^代表main.o func.o
main.o:main.c
$(CC) -c $^ -o $@ #$@代表main.o $^代表main.c
func.o:func.c
$(CC) -c $^ -o $@
通配符和模式匹配
- makefile也允许对目标文件名和依赖文件名进行类似正则表达式运算的模式匹配,主要使用的是% 匹配符(%表示在依赖文件列表当中匹配任意字符),例如将上述例子改写成
OBJS:=main.o func.o
CC:=gcc
main:$(OBJS)
$(CC) -o $@ $^
%.o:%.c #先在依赖文件列表当中匹配得到后缀为.o的文件,再根据.o文件的文件名找到同名的.c文件
# 这里如果使用*.c,那么就会在当前目录所有文件里面进行匹配
$(CC) -c $^ -o $@
- %也可以在变量内部进行查找替换
SRCS = test.c test1.c
OBJECTS = $(SRCS:%.c=%.o)
内置函数
- 格式:
$([function] [arguments])
- 使用wildcard函数可以使用通配符,找到所有满足通配符的文件名
SRCS:=$(wildcard *.c)
- 使用patsubst函数来实现模式文本替换
$(patsubst pattern,replacement,text)
OBJS:=$(patsubst %.c,%.o, $(SRCS))
- 使用%去匹配上一个规则的依赖
示例
SRCS:=$(wildcard *.c)
OBJS:=$(patsubst %.c, %.o, $(SRCS))
OUT:=main
$(OUT): $(OBJS)
gcc $^ -o $@
%.o: %.c
gcc -c $^
.PHONY: clean rebuild print
clean:
$(RM) *.o main
rebuild: clean main
print:
echo $(SRCS)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)