当前位置: 首页 > news >正文

Linux基础开发工具(make/Makefile篇)

目录

1.自动化构建-make/Makefile

1.背景

2.理解以及基本使用

1.是什么?快速的见一见make/makefile

2.makefile的基本概念---依赖关系,依赖方法

3.自动进行代码编写和清理

4.什么叫做不被执行?

1.默认老代码不做重新编译

2.make怎么知道bin和.c的新旧问题呢?

3.一些不同写法的Makfile以及推导

1.依赖链版Makefile Makefile

2.变量版Makefile Makefile

3.OBJ版Makefile Makefile

4.改进版Makefile Makefile


1.自动化构建-make/Makefile

1.背景

• 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力

• 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一 系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至 于进行更复杂的功能操作

• makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全 自动编译,极大的提高了软件开发的效率。

• make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这 个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile 都成为了一种在工程方面的编译方法。

• make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。

2.理解以及基本使用

1.是什么?快速的见一见make/makefile

make是一个命令
makefile是一个文件

2.makefile的基本概念---依赖关系,依赖方法

1 code:code.c 依赖关系---只表明了依赖关系
2 gcc -o code code.c 依赖方法---依赖方法
必须有tab键打头

这样就会形成makefile

3.自动进行代码编写和清理

先创建一个myproc.c,写入代码,再写一个Makefile文件

然后直接make就可

我们不光想让Makefile进行编译,也想让其进行清理

1 myproc:myproc.c 2 gcc -o myproc myproc.c 3 .PHONY:clean 4 clean: 5 rm -f myproc

保存退出后,直接make clean,就会清理了

1 myproc:myproc.c 2 gcc -o myproc myproc.c 3 .PHONY:clean 4 clean: 5 rm -f myproc

第三行clean 伪目标---表示总是被执行,对应的依赖关系和依赖方法

4、5行也是一组依赖关系,依赖方法,clean依赖一组空的文件表,那么rm -f就会自动执行

make命令扫描makefile命令的时候,从上往下扫描,默认形成第一个目标文件

所以一般把可执行文件放前面

那么为什么要在第三行clean前面加上.PHONY呢?

.PHONY相当于一个建议的关键字,它的作用是伪目标来表示总是被执行,对应的依赖关系和依赖方法

4.什么叫做不被执行?

1.默认老代码不做重新编译

我们发现第一次编译之后,就不再执行了

那么既然可以修饰clean,自然也可以修饰myproc

1 .PHONY:myproc 2 myproc:myproc.c 3 gcc -o myproc myproc.c 4 .PHONY:clean 5 clean: 6 rm -f myproc

这样被.PHONY修饰就可以总是被执行

如果有很多文件的话,不加.PHONY修饰,那么老文件就可以不被执行不重新编译,只执行修改的文件,也算是提高了效率

2.make怎么知道bin和.c的新旧问题呢?

之前提到过

文件=内容+属性

所以修改一个文件就可以修改内容或者属性要么两个都改

如果只修改文件的内容,我们称为Modify时间被更改

如果只修改文件的属性,我们称为Change时间被修改

如果只是单纯查看文件,Access时间被更新

看Modify时间

[user1@iZ5waahoxw3q2bZ 26-4-15.2]$ stat myproc.c File: ‘myproc.c’ Size: 78 Blocks: 8 IO Block: 4096 regular file Device: fd01h/64769d Inode: 1061380 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1001/ user1) Gid: ( 1001/ user1) Access: 2026-04-15 16:58:25.727086252 +0800 Modify: 2026-04-15 16:56:36.295258404 +0800 Change: 2026-04-15 16:56:36.295258404 +0800 Birth: -

我们修改一下myproc.c

[user1@iZ5waahoxw3q2bZ 26-4-15.2]$ stat myproc.c File: ‘myproc.c’ Size: 108 Blocks: 8 IO Block: 4096 regular file Device: fd01h/64769d Inode: 1061382 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1001/ user1) Gid: ( 1001/ user1) Access: 2026-04-15 17:30:09.736851047 +0800 Modify: 2026-04-15 17:30:09.736851047 +0800 Change: 2026-04-15 17:30:09.736851047 +0800 Birth: -

一般只要改Modify,内容属性都会被改

用chmod可以修改它的Change时间

[user1@iZ5waahoxw3q2bZ 26-4-15.2]$ chmod u+x myproc.c [user1@iZ5waahoxw3q2bZ 26-4-15.2]$ ll total 20 -rw-rw-r-- 1 user1 user1 74 Apr 15 17:21 Makefile -rwxrwxr-x 1 user1 user1 8440 Apr 15 17:21 myproc -rwxrw-r-- 1 user1 user1 108 Apr 15 17:30 myproc.c [user1@iZ5waahoxw3q2bZ 26-4-15.2]$ stat myproc.c File: ‘myproc.c’ Size: 108 Blocks: 8 IO Block: 4096 regular file Device: fd01h/64769d Inode: 1061382 Links: 1 Access: (0764/-rwxrw-r--) Uid: ( 1001/ user1) Gid: ( 1001/ user1) Access: 2026-04-15 17:30:50.265526796 +0800 Modify: 2026-04-15 17:30:09.736851047 +0800 Change: 2026-04-15 18:22:14.113943566 +0800 Birth: -

但是我们发现我们查看一下,然后stat连access时间都没变。不是说会变吗?


这是因为在Linux系统中查找一个内容或者属性比修改的比重要大的多的,这样如果查一下就改一下时间就可能会造成隐性的成本,过于高频把时间更新,系统中会有大量IO操作

所以改成查个几十次上百次文件,时间才会改变。

所以通过Modify时间可以知道文件新旧。

touch可以更改时间

[user1@iZ5waahoxw3q2bZ 26-4-15.2]$ ll total 20 -rw-rw-r-- 1 user1 user1 74 Apr 15 17:21 Makefile -rwxrwxr-x 1 user1 user1 8440 Apr 15 17:21 myproc -rw-rw-r-- 1 user1 user1 108 Apr 15 17:30 myproc.c [user1@iZ5waahoxw3q2bZ 26-4-15.2]$ touch myproc.c [user1@iZ5waahoxw3q2bZ 26-4-15.2]$ stat myproc.c File: ‘myproc.c’ Size: 108 Blocks: 8 IO Block: 4096 regular file Device: fd01h/64769d Inode: 1061382 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1001/ user1) Gid: ( 1001/ user1) Access: 2026-04-15 18:38:01.962633129 +0800 Modify: 2026-04-15 18:38:01.962633129 +0800 Change: 2026-04-15 18:38:01.962633129 +0800 Birth: -

可以看到touch一个以存在的文件,三个时间都更改了。

所以说之前.PHONY是因为,make调用gcc时告诉gcc忽略对比时间、忽略新旧问题,直接编译。

3.一些不同写法的Makfile以及推导

1.依赖链版Makefile

依赖链---推导链

Makefile
1 myproc:myproc.o 2 gcc myproc.o -o myproc 3 myproc.o:myproc.s 4 gcc -c myproc.s -o myproc.o 5 myproc.s:myproc.i 6 gcc -S myproc.i -o myproc.s 7 myproc.i:myproc.c 8 gcc -E myproc.c -o myproc.i 9 10 .PHONY:clean 11 clean: * 12 rm -f *.i *.s *.o myproc

make一下,编译过程就体现出来了

[user1@iZ5waahoxw3q2bZ 26-4-15.2]$ make gcc -E myproc.c -o myproc.i gcc -S myproc.i -o myproc.s gcc -c myproc.s -o myproc.o gcc myproc.o -o myproc [user1@iZ5waahoxw3q2bZ 26-4-15.2]$ ll total 48 -rw-rw-r-- 1 user1 user1 315 Apr 15 18:54 Makefile -rwxrwxr-x 1 user1 user1 8440 Apr 15 18:54 myproc -rw-rw-r-- 1 user1 user1 108 Apr 15 18:38 myproc.c -rw-rw-r-- 1 user1 user1 16913 Apr 15 18:54 myproc.i -rw-rw-r-- 1 user1 user1 1560 Apr 15 18:54 myproc.o -rw-rw-r-- 1 user1 user1 479 Apr 15 18:54 myproc.s

Makefile在翻译的时候,内部会维护一种栈结构的东西

Makefile文件就会被Makefile依次扫描从上到下,

首先根据依赖关系,比如上面,结果发现myproc.o不存在,就将这个入栈。

然后根据依赖关系继续向下,同理,不断入栈

到最后发现myproc.c有,然后执行这个命令,从而依次出栈并执行命令

从上到下扫描,从下到上递归式地执行形成最终的目标文件

这就是Makefile进行推导的最基本的规则

make是如何工作的,在默认的方式下,也就是我们只输入make命令。那么:

1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。

2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到 myproc 这 个文件,并把这个文件作为最终的目标文件。

3. 如果 myproc 文件不存在,或是 myproc 所依赖的后面的 myproc.o 文件的文件修改时间要 比 myproc 这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成 myproc 这个文件。

4. 如果 myproc 所依赖的 myproc.o 文件不存在,那么 make 会在当前文件中找目标为 myproc.o 文件的依赖性,如果找到则再根据那一个规则生成 myproc.o 文件。(这有点像一 个堆栈的过程)

5. 当然,你的C文件和H文件是存在的啦,于是 make 会生成 myproc.o 文件,然后再用 myproc.o 文件声明 make 的终极任务,也就是执行文件 hello 了。

6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个 目标文件。

7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。

8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对 不起,我就不工作啦。

所以里面如果不小心写错什么,如下就写错第四行最后o变x

Makefile

1 myproc:myproc.o 2 gcc myproc.o -o myproc 3 myproc.o:myproc.s 4 gcc -c myproc.s -o myproc.x 5 myproc.s:myproc.i 6 gcc -S myproc.i -o myproc.s 7 myproc.i:myproc.c 8 gcc -E myproc.c -o myproc.i 9 10 .PHONY:clean 11 clean: * 12 rm -f *.i *.s *.o myproc

从而无法通过

所以说不太推荐这样写,这里只是展示一下编译过程等。

2.变量版Makefile

Makefile

BIN=myproc # 定义变量 BIN,值为 myproc(通常代表最终可执行文件名) CC=gcc # 定义变量 CC,值为 gcc(指定 C 编译器) SRC=myproc.c # 定义变量 SRC,值为 myproc.c(源文件) FLAGS=-o # 定义变量 FLAGS,值为 -o(编译选项,用于指定输出文件) .PHONY: test # 声明 test 是一个伪目标(不生成实际文件) test: # test 目标的规则 @echo $(BIN) # 打印变量 BIN 的值(myproc) @echo $(CC) # 打印变量 CC 的值(gcc) @echo $(SRC) # 打印变量 SRC 的值(myproc.c) @echo $(FLAGS)# 打印变量 FLAGS 的值(-o)

在前面加上@就可以变成静默式

可以把这些理解为宏

Makefile

1 BIN=myproc 2 CC=gcc 3 SRC=myproc.c 4 FLAGS=-o 5 RM=rm -f 6 7 8 $(BIN):$(SRC) 9 $(CC) $(FLAGS) $(BIN) $(SRC) 10 .PHONY: 11 clean: 12 $(RM) $(BIN) 13 14 .PHONY:test 15 test: 16 @echo $(BIN) 17 @echo $(CC) 18 @echo $(SRC) 19 @echo $(FLAGS) 20 @echo $(RM)

Makefile
1 BIN=myproc 2 CC=gcc 3 SRC=myproc.c 4 FLAGS=-o 5 RM=rm -f 6 7 8 $(BIN):$(SRC) 9 @$(CC) $(FLAGS) $@ $^ 10 @echo "linking ... $^ to $@" 11 .PHONY: 12 clean: 13 @$(RM) $(BIN) 14 @echo "remove ... $(BIN)" 15 16 .PHONY:test 17 test: 18 @echo $(BIN) 19 @echo $(CC) 20 @echo $(SRC) 21 @echo $(FLAGS) 22 @echo $(RM)

在前面加上@就可以变成静默式

$@对应依赖的众多文件 $(BIN)

$^对应依赖的目标文件 $(SRC)

3.OBJ版Makefile

1 BIN=myproc.exe 2 CC=gcc 3 SRC=myproc.c 4 OBJ=myproc.o 5 FLAGS=-o 6 RM=rm -f 7 8 $(BIN):$(OBJ) 9 $(CC) $(FLAGS) $@ $^ 10 $(OBJ):$(SRC)

如果有几百个-o -c呢?

所以应该改一下

Makefile
1 BIN=myproc.exe 2 CC=gcc 3 SRC=myproc.c 4 OBJ=myproc.o 5 LFLAGS=-o 6 FLAGS=-c 7 RM=rm -f 8 9 $(BIN):$(OBJ) 10 @$(CC) $(LFLAGS) $@ $^ 11 @echo "linking ... $^ to $@" 12 %.o:%.c 13 @$(CC) $(FLAGS) $< 14 @echo "compling ... $< to $@" 15 16 .PHONY:clean 17 clean: 18 $(RM) $(OBJ) $(BIN)

在前面加上@就可以变成静默式

%:把当前路径下的所有.o/.c依次展开
$@ - 目标文件 (Target)
$< - 第一个依赖文件 (First prerequisite)
$^ - 所有依赖文件 (All prerequisites)

4.改进版Makefile

1 BIN=myproc.exe 2 CC=gcc 3 SRC=$(shell ls *.c) 4 OBJ=myproc.o 5 LFLAGS=-o 6 FLAGS=-c 7 RM=rm -f 8 9 $(BIN):$(OBJ) 10 @$(CC) $(LFLAGS) $@ $^ 11 @echo "linking ... $^ to $@" 12 %.o:%.c 13 @$(CC) $(FLAGS) $< 14 @echo "compling ... $< to $@" 15 16 .PHONY:clean 17 clean: 18 $(RM) $(OBJ) $(BIN) 19 20 .PHONY:test 21 test: 22 @echo $(SRC) 23 @echo $(OBJ)

shell ls *.c 用于列出当前目录下所有以.c结尾的文件

Makefile自己也有一种这样的函数wildcard

Makefile
1 BIN=myproc.exe 2 CC=gcc 3 #SRC=$(shell ls *.c) 4 SRC=$(wildcard *.c) 5 OBJ=$(SRC:.c=.o) 6 LFLAGS=-o 7 FLAGS=-c 8 RM=rm -f 9 10 $(BIN):$(OBJ) 11 @$(CC) $(LFLAGS) $@ $^ 12 @echo "linking ... $^ to $@" 13 %.o:%.c 14 @$(CC) $(FLAGS) $< 15 @echo "compling ... $< to $@" 16 17 .PHONY:clean 18 clean: 19 $(RM) $(OBJ) $(BIN) 20 21 .PHONY:test 22 test: 23 @echo $(SRC) 24 @echo $(OBJ)

在前面加上@就可以变成静默式

%:把当前路径下的所有.o/.c依次展开
$@ - 目标文件 (Target)
$< - 第一个依赖文件 (First prerequisite)
$^ - 所有依赖文件 (All prerequisites)

SRC=$(wildcard *.c) ---显示当前目录下所有的.c文件名

OBJ=$(SRC:.c=.o) ---SRC内部的文件名.c ->文件名.o

测试一下

新增一百个文件

le就是小于等于

count=1; while [ $count -le 100 ]; do touch code${count}.c; let count++; done

make

make clean

今天我们make/Makefile的学习就到此结束,期待我们下次再见!(下篇会根据make/Makefile写一个简单的进度条)

http://www.cnnetsun.cn/news/1912089.html

相关文章:

  • Ubuntu Autoinstall Generator:三步快速上手自动化部署工具
  • 遗传算法与免疫算法求解物流配送中心选址问题,附详细注释与源码(Matlab编写
  • 好写作AI“毕业护航舰”:驶向学术彼岸的智能领航者
  • Jetson Orin NX实战:打造无感启动的YOLO+ROS一体化机器人视觉系统
  • Cursor Pro终极破解教程:免费解锁AI编程助手完整指南
  • 测试右移实战:生产环境监控技巧
  • Windows系统精简优化终极指南:告别臃肿,重获流畅体验
  • 保姆级教程:用Shell脚本一键搞定nuScenes v1.0数据集下载与解压(附避坑指南)
  • SITS2026正式发布:3类高危API设计反模式、2套工业级适配模板与实时调用性能压测数据全公开
  • PyTorch可视化神器pytorchviz实战:从模型构建到导出ONNX全流程详解
  • 多模态LLM推理链路混沌实验全记录,深度复现跨模态对齐失效、特征坍缩与token洪水攻击
  • USBCopyer终极指南:Windows平台USB自动备份工具的完整使用教程
  • 软件设计师——McCabe环路复杂度在代码审查与重构中的实战应用
  • 工程师必看:如何用磁珠解决PCB设计中的高频噪声问题(附实测案例)
  • 数字电子钟设计避坑指南:CD4511驱动数码管常见问题解决方案
  • 2026年最火的开发框架:React、Vue还是新王者?——软件测试从业者的专业视角
  • 【python-sc2】从零到一:构建你的星际争霸2 AI智能体核心数据感知与决策模块
  • 20个核心AI概念拆解:小白也能看懂的大模型世界,速收藏
  • 用FPGA和Ego1开发板,从零搭建一个能识别红绿灯的超声波避障小车(含完整代码)
  • 步进电机控制中的常见问题及解决方案:基于台达PLC的实践经验
  • AI文案合规红线在哪?SITS2026系统内置《广告法+网信办AI生成内容指南》双引擎校验机制(内测版策略文档首度流出)
  • 嵌入式调试效率翻倍:手把手教你为STM32F405配置J-Link RTT(附性能对比与避坑指南)
  • mysql主键索引与二级索引区别_mysql索引结构设计优选
  • brackets怎么运行html_Brackets编辑器如何实时预览HTML
  • Sunshine游戏串流完整指南:5步实现自托管游戏串流服务器部署
  • Windows/Mac/Linux全平台保姆级教程:从零配置OpenCode到成功调用Gemini-3
  • 避开这些坑!GD32F303的ADC+DMA+定时器采集方案配置详解与性能优化
  • Hermes 智能体完全实战指南
  • 实测对比:五款免费音视频转SRT字幕工具,谁更适合你?(通义千问、飞书妙记、卡卡字幕助手、AsrTools)
  • AT32F421实战---SPI驱动CH395Q构建简易物联网网关