【Linux基础开发工具 (五)】详解自动化构建:make / Makefile


🎬 个人主页艾莉丝努力练剑
专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录
Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬 艾莉丝的简介:


🎬 艾莉丝的Linux专栏简介:
​​​​


文章目录

  • [4 ~> 自动化工程构建:make / Makefile](#4 ~> 自动化工程构建:make / Makefile)
    • [4.1 自动化构建:make / Makefile](#4.1 自动化构建:make / Makefile)
      • [4.1.1 概念](#4.1.1 概念)
      • [4.1.2 熟悉环节:如何使用](#4.1.2 熟悉环节:如何使用)
      • [4.1.3 清理项目(看完下转【构建工程 + 清理工程】)](#4.1.3 清理项目(看完下转【构建工程 + 清理工程】))
      • [4.1.4 逻辑图:依赖关系和依赖方法](#4.1.4 逻辑图:依赖关系和依赖方法)
      • [4.1.5 编译流程解析(从下往上执行)](#4.1.5 编译流程解析(从下往上执行))
      • [4.1.6 关键文件关系](#4.1.6 关键文件关系)
      • [4.1.7 Makefile语法特征](#4.1.7 Makefile语法特征)
      • [4.1.8 Makefile的语法推导过程](#4.1.8 Makefile的语法推导过程)
    • [4.2 构建工程 + 清理工程(详解)](#4.2 构建工程 + 清理工程(详解))
      • [4.2.1 依赖列表可以为空](#4.2.1 依赖列表可以为空)
      • [4.2.2 可以有多个依赖关系](#4.2.2 可以有多个依赖关系)
      • [4.2.3 make如何知道code.c是否需要被重新编译?](#4.2.3 make如何知道code.c是否需要被重新编译?)
      • [4.2.4 什么叫做总是被执行?](#4.2.4 什么叫做总是被执行?)
    • [4.3 出现了这样的崩溃是什么意思?](#4.3 出现了这样的崩溃是什么意思?)
      • [4.3.1 核心问题解析](#4.3.1 核心问题解析)
      • [4.3.2 两种可能原因](#4.3.2 两种可能原因)
      • [4.3.3 操作选项说明](#4.3.3 操作选项说明)
      • [4.3.4 推荐操作步骤](#4.3.4 推荐操作步骤)
      • [4.3.5 注意事项](#4.3.5 注意事项)
    • [4.4 [M / m]akefile最佳实践和实用语法](#4.4 [M / m]akefile最佳实践和实用语法)
    • [4.5 适度扩展语法](#4.5 适度扩展语法)
  • 本文代码完整演示
  • 博主手记
  • 结尾


4 ~> 自动化工程构建:make / Makefile

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

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

4.1 自动化构建:make / Makefile

4.1.1 概念

make:命令------

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

makefile / Makefile(首字母大不大小写无所谓):一个文件------描述的是如何编译当前工程------

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

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

4.1.2 熟悉环节:如何使用

bash 复制代码
[Alice@VM-4-17-centos Ludy]$ vim code.c
[Alice@VM-4-17-centos Ludy]$ ll
total 12
-rw-rw-r-- 1 Alice Alice 277 Nov 20 13:54 code.c
-rw-rw-r-- 1 Alice Alice 166 Nov 20 13:52 Makefile
-rw-rw-r-- 1 Alice Alice  65 Nov 20 13:47 Makefile-bk
[Alice@VM-4-17-centos Ludy]$ make
gcc -E code.c -o code.i
gcc -S code.i -o code.s
gcc -c code.s -o code.o
gcc code.o -o code 
code.o -o code
make: execvp: code.o: Permission denied
make: *** [code] Error 127
[Alice@VM-4-17-centos Ludy]$ cat Makefile
code:code.o
	gcc code.o -o code 
	code.o -o code
code.o:code.s
	gcc -c code.s -o code.o
code.s:code.i
	gcc -S code.i -o code.s
code.i:code.c
	gcc -E code.c -o code.i

4.1.3 清理项目(看完下转【构建工程 + 清理工程】)

工程是需要被清理的。

像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令---"makeclean",以此来清除所有的目标文件,以便重编译。

但是一般我们这种clean的目标文件,我们将它设置为伪目标,用。PHONY修饰,伪目标的特性是,总是被执行的。

bash 复制代码
code:code.c
     gcc -o code code.c
.PHONY:clean                                                                                                                            
clean:
     rm -f code

把上面的指令写进Makefile文件,直接用一个指令就能完成清理工作------

bash 复制代码
[Alice@VM-4-17-centos Ludy]$ make clean

.PHONY:让make忽略源⽂件和可执⾏⽬标⽂件的M时间对⽐。

4.1.4 逻辑图:依赖关系和依赖方法

一张图搞定------

make命令内部维护了一个栈结构,因为栈是先进后出后进先出的,文件还不存在------入栈,找到文件------出栈。

4.1.5 编译流程解析(从下往上执行)

阶段 命令 输入文件 输出文件 作用说明
预处理 gcc -E code.c -o code.i code.c code.i 处理宏定义、头文件包含等预处理指令
编译 gcc -S code.i -o code.s code.i code.s 将C代码转换为汇编语言
汇编 gcc -c code.s -o code.o code.s code.o 将汇编代码转换为二进制目标文件
链接 gcc code.o -o code code.o code 链接目标文件生成可执行程序

4.1.6 关键文件关系

bash 复制代码
code.c →[预处理]→ code.i →[编译]→ code.s →[汇编]→ code.o →[链接]→ code

.c:源代码文件(人可读)

.i:预处理后文件(保留C语法)

.s:汇编语言文件(机器指令的文本表示)

.o:目标文件(二进制中间产物,不可直接执行)

无后缀的code:最终可执行文件

4.1.7 Makefile语法特征

下图左侧的code: code.o等行为的Makefile规则:

目标: 依赖 表示"生成目标文件需要依赖哪些文件" ;

例如code.o: code.s表示编译code.o需要先有code.s文件。

这个流程展示了编译器"一遍编译"的工作原理,即从源代码(.c)到可执行文件(二进制可执行文件)的完整转换链条。需要实际操作时,可以直接执行gcc code.c -o code一步完成,但图片拆分为多步更清晰地展示了内部过程。

4.1.8 Makefile的语法推导过程

bash 复制代码
myproc:myproc.o 
	 gcc myproc.o -o myproc
myproc.o:myproc.s 
	 gcc -c myproc.s -o myproc.o
myproc.s:myproc.i 
	 gcc -S myproc.i -o myproc.s
myproc.i:myproc.c 
	gcc -E myproc.c -o myproc.i
 
.PHONY:clean 
clean: 
	 rm -f *.i *.s *.o myproc

编译一下------

bash 复制代码
$ 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

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只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。

4.2 构建工程 + 清理工程(详解)

4.2.1 依赖列表可以为空

4.2.2 可以有多个依赖关系

4.2.3 make如何知道code.c是否需要被重新编译?

4.2.4 什么叫做总是被执行?

bash 复制代码
$ stat XXX
 File: 'XXX'
 Size: 987 Blocks: 8 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 1321125 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ whb) Gid: ( 1000/ whb)
Access: 2024-10-25 17:05:30.430619002 +0800
Modify: 2024-10-25 17:05:25.940595116 +0800
Change: 2024-10-25 17:05:25.940595116 +0800
⽂件 = 内容 + 属性
Modify: 内容变更,时间更新
Change:属性变更,时间更新
Access:常指的是⽂件最近⼀次被访问的时间。在Linux的早期版本中,每当⽂件被访问时,其
atime都会更新。但这种机制会导致⼤量的IO操作。具体更新原则,不做过多解释。

.PHONY: 让make忽略源文件和可执行目标文件的M时间对比。

4.3 出现了这样的崩溃是什么意思?

这是Vim编辑器的交换文件(Swap File) 提示信息,通常在以下情况出现:

4.3.1 核心问题解析

什么是交换文件?

Vim在编辑文件时会自动创建临时交换文件(.swp结尾),用于崩溃时恢复数据。截图中显示的交换文件路径为:

bash 复制代码
~/.local/share/nvim/swap//%home%Alice%Ludy%Makefile.swp

为什么会出现这个提示?

该交换文件由用户Alice在 11月25日23:46 创建,对应原文件是 ~Alice/Ludy/Makefile

关键矛盾:现在打开的Makefile修改时间是 11月26日15:43(比交换文件更新),系统检测到文件状态不一致

4.3.2 两种可能原因

另一程序正在编辑:可能有其他Vim窗口或用户(Alice)同时打开了该文件,直接编辑可能导致内容冲突

上次编辑崩溃:之前的Vim会话异常退出(如强制关闭、断电),交换文件保留了未保存的修改

4.3.3 操作选项说明

选项 含义 建议场景
(O)pen Read-Only 只读打开,不修改文件 仅查看内容,避免冲突
(E)dit anyway 强制编辑,忽略交换文件 确认无人使用且不需要恢复时
®ecover 从交换文件恢复数据 需要找回上次未保存的修改
(D)elete it 删除交换文件 确认原文件已更新且无恢复需求
(Q)uit / (A)bort 退出/中止操作 暂时不处理该文件

4.3.4 推荐操作步骤

先确认是否有其他程序正在编辑 Makefile(如询问Alice);

若文件已手动保存最新版本,直接按 D 删除交换文件,下次打开不再提示;

若需恢复上次崩溃前的内容,按 R 恢复后,记得手动删除交换文件。

4.3.5 注意事项

交换文件是临时文件,正常退出Vim时会自动删除;

若频繁出现此提示,可能是Vim配置或文件权限问题,可检查 ~/.local/share/nvim/swap 目录权限。

4.4 [M / m]akefile最佳实践和实用语法

如下图所示------

4.5 适度扩展语法

如下图所示------




bash 复制代码
  1 Bin=code.exe
  2 #Src=$(shell ls * .c)   # 做法1
  3 Src=$(wildcard *.c)     # 做法2(推荐)
  4 Obj=$(Src:.c=.o)
  5 Echo=echo
  6 cc=gcc
  7 Em=rm -f
  8 Flags=-s -Wall
  9 LD_Flags=-o
 10 
 11 $(Bin):$(Obj)
 12     @$(Echo)"我要开始链接了...$(Obj)-> $(Bin)"
 13     @$(cc) $(LD_Flags) $@ @^
 14 %.o:%.c                                 
 15     @$(Echo) "我要开始编译了...$< -> $@"
 16     @$(cc) $(Flags) $<
 17 .PHONY:clean
 18 clean:                                                                                                                                                              
 19     $(Rm) $(Obj) $(Bin)
 20             
 21 .PHONY:debug
 22 debug:                 
 23     @echo "Bin: $(Bin)"
 24     @echo "Obj: $(Obj)"
 25     @echo "Src: $(Src)"
 26            
 27 #.PHONY:code                                                                                                                                                        
 28 #code:code.c
 29 #   gcc code.c -o code
 30 #                        
 31 ##.PHONY:clean           
 32 #clean:                  
 33 #   rm -f code
bash 复制代码
BIN=proc.exe # 定义变量  
CC=gcc 
#SRC=$(shell ls *.c) 	# 采⽤shell命令⾏⽅式,获取当前所有.c⽂件名 
SRC=$(wildcard *.c) 	# 或者使⽤ wildcard 函数,获取当前所有.c⽂件名 
OBJ=$(SRC:.c=.o) 			# 将SRC的所有同名.c 替换 成为.o 形成⽬标⽂件列表 
LFLAGS=-o 						# 链接选项 
FLAGS=-c 						# 编译选项 
RM=rm -f 						# 引⼊命令 

$(BIN):$(OBJ) 
	 @$(CC) $(LFLAGS) $@ $^ 			  # $@:代表⽬标⽂件名。 $^: 代表依赖⽂件列表 
	 @echo "linking ... $^ to $@" 
%.o:%.c 												 # %.c 展开当前⽬录下所有的.c. %.o: 同时展开同名.o
	@$(CC) $(FLAGS) $< 						 # %< 对展开的依赖.c⽂件,⼀个⼀个的交给gcc。 
	@echo "compling ... $< to $@"  # @:不回显命令 
.PHONY:clean 
clean:
	 $(RM) $(OBJ) $(BIN) 					# $(RM): 替换,⽤变量内容替换它 
 
.PHONY:test 
test: 
	 @echo $(SRC) 
	 @echo $(OBJ)

本文代码完整演示

Makefile-backup

bash 复制代码
1 #code:code.o 
  2 #   gcc code.o -o code
  3 #code.o:code.s
  4 #   gcc -c code.s -o code.o
  5 #code.s:code.i
  6 #   gcc -S code.i -o code.s
  7 #code.i:code.c
  8 #   gcc -E code.c -o code.i
  9 #
 10 #.PHONY:clean
 11 #clean:
 12 #   rm -f code.i code.s code.o code
 13 
 14 code.exe:code.o
 15     gcc code.o -o code.exe
 16 code.o:code.c
 17     gcc -c code.c
 18 .PHONY:clean
 19 clean:
 20     rm -f code.o code.exe
 21 
 22 #.PHONY:code
 23 #code:code.c
 24 #   gcc code.c -o code
 25 #
 26 #.PHONY:clean:
 27 #clean:
 28 #   rm -f code      

Makefile

bash 复制代码
  1 Bin=code.exe
  2 #Src=$(shell ls *.c) # 做法1
  3 Src=$(wildcard *.c) # 做法2
  4 Obj=$(Src:.c=.o)
  5 Echo=echo
  6 cc=gcc
  7 Rm=rm -f
  8 Flags=-c -Wall
  9 LD_Flags=-o
 10         
 11 $(Bin):$(Obj) 
 12     @$(Echo) "我要开始链接了...$(Obj) -> $(Bin)"
 13     @$(cc) $(LD_Flags) $@ $^
 14 %.o:%.c      
 15     @$(Echo) "我要开始编译了...$< -> $@"        
 16     @$(cc) $(Flags) $<      
 17 .PHONY:clean
 18 clean:                                  
 19     $(Rm) $(Obj) $(Bin)
 20             
 21 .PHONY:debug
 22 debug:                 
 23     @echo "Bin: $(Bin)"                                                                                                                 
 24     @echo "Obj: $(Obj)"
 25     @echo "Src: $(Src)"
 26 
 27 
 28                        
 29 #.PHONY:code
 30 #code:code.c
 31 #   gcc code.c -o code
 32 #
 33 #.PHONY:clean
 34 #clean:
 35 #   rm -f code
                        

效果展示

bash 复制代码
[Alice@VM-4-17-centos Ludy]$ make
我要开始编译了...code.c -> code.o
我要开始链接了...code.o -> code.exe
[Alice@VM-4-17-centos Ludy]$ make clean
rm -f code.o code.exe
[Alice@VM-4-17-centos Ludy]$ ll
total 60
-rwxrwxr-x 1 Alice Alice  6264 Nov 26 03:14 a.out
-rwxrwxr-x 1 Alice Alice  8361 Nov 26 17:34 code
-rw-rw-r-- 1 Alice Alice   499 Nov 26 17:35 code.c
-rw-rw-r-- 1 Alice Alice 17168 Nov 25 23:10 code.i
-rw-rw-r-- 1 Alice Alice   711 Nov 25 23:10 code.s
-rw-rw-r-- 1 Alice Alice   524 Nov 26 17:34 Makefile
-rw-rw-r-- 1 Alice Alice   415 Nov 26 14:27 Makefile-backup
-rw-rw-r-- 1 Alice Alice    65 Nov 20 14:33 Makefile-bk

博主手记


结尾

uu们,本文的内容到这里就全部结束了,艾莉丝再次感谢您的阅读!

结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!

往期回顾

【Linux基础开发工具 (四)】Linux编程基础:深入理解gcc / g++编译与链接原理

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა

相关推荐
SongYuLong的博客35 分钟前
C++基于jsoncpp开源库json数据操作
开发语言·c++·json
Predestination王瀞潞38 分钟前
安装了Anaconda在系统终端却无法使用python命令
linux·开发语言·python
❀͜͡傀儡师2 小时前
docker部署mariadb 12.1.2版本,支持arm架构
运维·docker·容器·mariadb
艾莉丝努力练剑3 小时前
【C++:异常】C++ 异常处理完全指南:从理论到实践,深入理解栈展开与最佳实践
java·开发语言·c++·安全·c++11
快乐zbc9 小时前
【C++ 基础】:给定一个指针 p,你能判断它是否指向合法的对象吗?
c++
青青草原技术员灰太狼9 小时前
Nginx的https搭建
linux·服务器·网络
xu_yule9 小时前
网络和Linux网络-4(应用层)序列化和反序列化(网络计算器)
linux·网络
宇钶宇夕9 小时前
三旺 INP314T 网关快速入门指南:从登录到基础配置
运维·自动化·软件工程
sulikey9 小时前
C++类和对象(下):初始化列表、static、友元、内部类等核心特性详解
c++·static·初始化列表·友元·匿名对象·内部类·编译器优化