如何编写Makefile文件
一、预备知识
1、从我们编写的源文件到最终的可执行文件(.exe/a.out)的过程。
源文件->预处理->编译->汇编->链接
2、每个过程产生的文件
gcc -E xxx.c -o xxx.i:预处理,生成预处理文件(.i)。
gcc -S xxx.c/xxx.i -o xxx.s:编译,生成汇编文件(.s)。
gcc -c xxx.c/xxx.i/xxx.s -o xxx.o:汇编,生成目标文件(.o)。
gcc xxx.c/xxx.i/xxx.s/xxx.o -o xxx:链接,生成可执行程序(a.out)。
3、Gcc命令
-E:生成预编译文件命令。
-S:生成汇编文件命令。
-c:生成“.o”文件命令。
-o:指定生成的文件名的命令。
4、注意
A、生成的预编译文件(.i)需要用“-o”命令手动指定文件名称,否则不会生成“.i”文件,而是直接打印出来文件内容。其他文件若不指定名称则会生成同名的文件。
B、生成的可执行文件默认为“a.out”,一般指定文件名增加可读性。
C、预编译文件和汇编文件命令字母要大写。
二、初识Makefile
(1)、两个特殊情况
A、“all”目标通常是Makefile中的第一个目标,名字并非是固定的,习惯这样写增加可读性。注意一般“all”目标只有依赖没有要执行的规则(命令),所以并不生成“all”文件。
B、“clean”目标通常是Makefile文件中最后一个目标,注意一般“clean”目标只有用户编写的规则(命令),而没有依赖文件,且规则是用于清理产生的文件,所以并不会生成“clean”文件。
C、其它:Makefile文件名,大小写都可。make命令后面可以添加目标名,用于指定构建的目标,如果只是在命令行写入make,而不指定目标,它将会默认构建第一个目标“all”。
(2)、只生成一个可执行文件
1、Makefile基本规则。
点击查看
targets(目标):prerequisites(依赖) command(命令) *********************************** 注意命令前是个Tab键,不是空格
2、Makefile文件
点击查看
all:test test:test.c gcc test.c -o test clean: rm -f test
3、注释
A、“all:test”:“all”是目标名,“test”是构建“all”所需的依赖文件,依赖可以是多个,可以不指定依赖但指定的依赖必须存在,若不存在但可以通过存在的文件通过规则推导生成亦可就相当于间接存在,不然会报错。
B、“test:test.c”:“test”是目标名,“test.c”是构建“test”所需的依赖文件,依赖文件必须存在,如果不存在可以通过存在的文件生成,不然会报错。
C、“gcc test.c -o test”:构建“test”文件命令,以tab键开头,命令上面已介绍。
D、“clean:”:没有依赖。
E、“rm -f test”:“make clean”执行的命令,删除test文件,可以跟多个文件名,以空格隔开。
4、由图2知,只有一个目标“test”,所以只生成一个可执行文件“test”。由图3知,开始只有Makefile和源文件,执行make命令后生成可执行文件test,运行“test”打印文字,执行make clean清理生成的test,基本流程就是这样。

图1 编写的源程序

图2 编写的Makefile文件

图3 执行过程
(3)、生成一个可执行文件及其它你需要的过程文件(.i/.s/.o),需要那个添加那个。
1、这里我将“.i,.s,.o,test”均生成,图5是执行过程,供参考。

图4 编写的Makefile文件

图5 执行过程
三、简化Makefile
1、可以通过自定义变量、调用函数、调用shell命令来简化Makefile文件的编写。
2、常用简化Makefile编写的命令、函数、shell命令。
%:应用于Makefile文件中的匹配符
*:应用于系统中的匹配符,可与wildcard结合使用。
$:变量引用、函数调用、shell命令
$@:代表目标的名字
$^:构造目标的所有依赖
$<:构造目标的第一项依赖
$?:构造目标所需依赖中发生更新的依赖
$(basename 文件名):获取文件名前缀,如$(basename test.c),得test。
$(wildcard 某路径下匹配的文件):扩展通配符,可用于查找指定后缀文件,如:$(wildcard *.c ~/*.c),可指定多个路径,默认为当前路径。
$(subst str1,str2,target):字符串替换函数,将目标target中str1代表的字符替换为str2代表的字符
$(patsubst pattern1,pattern2,target):模式替换函数,将目标target中符合pattern1的元素替换为pattern2类型的元素。
$(filter pattern1 pattern2……,target):过滤函数,将目标target中不符合pattern的元素过滤掉,可以设置多个模式。
$(shell find ./ -name "*.c"):shell命令,搜索当前路径下后缀为“.c”的文件。
3、Makefile文件
点击查看
cc = gcc src = $(wildcard *.c ~/*.c) file_S = $(patsubst %.c,%.s,$(src)) des = test $(file_S) all:$(des) test:$(src) $(cc) $^ -o test %.s:%.c $(cc) -S $^ clean: rm -f $(des)
4、注释
cc = gcc:定义变量cc,可通过“$”引用gcc,下面均类似。
$(wildcard *.c ~/*.c):搜索当前目录和~/目录下所有的.c文件,返回以空格为间隔的文件名列表。
%.s:%.c:所有后缀为“.s”的文件的依赖为与之同名的后缀为“.c”的文件,即相当于“test.s:test.c test1.s:test1.c”。
rm -f $(des):清除所有生成的文件,即$(des)代表的所有文件。
5、部分截图

图1 test1.c

图2 test.h

图3 test.c

图4 Makefile执行流程
6、注意
A、Makefile文件会首先构造一个没有匹配符的目标,若整个文件没有不含匹配符的目标则可能会报找不到目标的错误,如以下Makefile文件就会出错。
点击查看
%.o:%.c gcc $< -o $@
四、利用Makefile
1、批量编译不在同一路径下的“.c”文件,生成可执行文件及汇编文件。
2、Makefile文件
点击查看
cc = gcc src = $(wildcard ~/workplace/file_1/*.c ./file_2/*.c) des = $(patsubst %.c,%,$(src)) file_S = $(patsubst %.c,%.s,$(src)) all:$(des) $(file_S) %.s:%.c $(cc) -E $^ -o $@ clean: rm -f $(des) $(file_S)
3、部分截图

图1 初始文件详情

图2 执行make命令

图3 执行后文件详情

图4 清理文件
4、截图说明
A、图1显示file_1文件夹和file_2文件夹下各有三个“.c”源文件。
B、图2显示执行make命令后Makefile文件执行的编译命令。
C、图3显示执行make命令后生成了“.s”汇编文件和可执行文件。
D、图4显示为清理生成的“.s”汇编文件和可执行文件,恢复文件夹的初始状态。
5、尝试运行
A、可执行文件运行成功,Makefile文件编写成功。

图5 数组右移3位
作者:知微smile
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器