📝前言:
这篇文章我们来讲讲Linux开发工具——make/makefile:
🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记,C语言入门基础,python入门基础,C++刷题专栏
目录
- 一,make/makefile基础介绍
- make
- Makefile
- 二,具体示例
- 1. 基础示例
- 1.1 时间戳
- 1.2 是否重新编译判断
- 2. 进阶1——依赖链
- 3. 进阶2——变量替换
- 4. 进阶3——多文件
- 4.1 获取当前目录下的所有文件
- 执行的命令不回显
- 4.2 %<
- 4.3 模式规则
一,make/makefile基础介绍
make/Makefile用于自动化构建和管理项目。
make
- 功能:make是一个命令工具,它根据
Makefile中定义的规则来自动构建和管理项目。它可以自动检测源文件的修改情况,并仅重新编译那些需要更新的目标文件,从而大大提高了编译效率。 - 工作原理:
make通过读取Makefile中的规则,分析目标文件和依赖文件之间的关系(简称依赖关系)。然后,它根据文件的时间戳(modify时间)来判断哪些文件需要重新编译。如果一个依赖文件的修改时间晚于目标文件,或者目标文件不存在,make就会执行相应的命令来更新目标文件。 - 使用方法:在项目目录中,只需在命令行中输入
make命令,它就会自动查找当前目录下的Makefile,并按照其中的规则进行构建。如果Makefile中有多个目标,make默认构建第一个规则,后面的要指定要构建的特定目标,例如make target_name。
Makefile
- 作用:Makefile是一个文本文件,它包含了一系列规则,用于告诉
make工具如何构建项目。它定义了项目中的目标文件、依赖文件以及构建目标所需的命令(方法)。 - 基本结构:
Makefile由一系列规则组成,每个规则通常包含以下部分:- 目标:通常是要构建的文件,如可执行文件、目标文件或库文件。也可以是一个抽象的目标,如
clean用于清理生成的文件。 - 依赖:目标所依赖的文件列表。
- 命令:用于构建目标的命令。命令必须以
Tab键开头。
- 目标:通常是要构建的文件,如可执行文件、目标文件或库文件。也可以是一个抽象的目标,如
(也可以认为:规则 = 依赖关系 + 依赖方法)
二,具体示例
1. 基础示例
先对前面介绍到的基础的概念和用法做演示。
步骤:
- 现在当前目录下创建
Makefile文件:touch ./Makefile - 根据需求,编写
Makefile

解释:
code.exe是目标文件,code.c是源文件(这是一组依赖关系,code.exe依赖code.c)Tab开头,后面写依赖方法(构造目标用的命令)- 一个抽象的目标
clean,可以没有所依赖的文件列表 - 依赖方法
调用示例:

make默认调用第一个规则,即code.exe:code.c这一个make clean,调用clean对应的规则- 为什么连续调用
make会失败呢?
因为:make根据文件modify的时间戳来判断哪些文件需要重新编译。
1.1 时间戳
那什么是modify时间戳,?
stat查看文件时间属性

Access:访问时间,当文件被查看,如cat的时候会被修改(但有一些系统会优化,即:多次查看后才修改,为了减少磁盘I/O操作)Modify:修改时间,代表文件最后一次被修改的时间,即内容有所改动,Modify就会改变Change:变更时间,当文件属性有变化的时候会改变Birth:诞生时间,创建时的时间
1.2 是否重新编译判断
当源文件的modify时间相比目标文件“旧”,代表没有源文件没有修改,则不重新编译
当源文件的modify时间相比目标文件“新”,代表源文件修改了,则重新编译

如图:我们在没有执行make之前,只有源文件1,执行完后,有了目标文件1,时间关系如上。此时再执行make就不会再重新编译
如果我们更改了源文件:

这时候,代表有新内容,目标文件就需要重新编译
那有没有办法让make无视时间戳,让依赖方法总是被执行呢?
有的兄弟,有的。那就是特殊目标声明:.PHONY,将目标标记为伪目标。

此时,make 不用去检查伪目标对应的文件是否存在、是否更新,而是直接执行命令

2. 进阶1——依赖链


依赖链的结构就像一个栈:
- 检查其依赖
code.o,发现code.o不存在,则依赖方法入栈 - 检查
code.o的依赖code.s,发现code.s不存在,则依赖方法也入栈 - …
- 检查
code.i的依赖code.c,发现code.c存在,则执行code.i的依赖方法 - 将栈中依赖方法依次执行
3. 进阶2——变量替换
定义变量:
=:递归展开变量(不会在定义变量时立即对右侧的表达式进行求值,而是在使用该变量时才进行求值。这意味着变量的值可能会因为其他变量的改变而改变)
:=:简单展开变量(在定义变量时就对右侧的表达式进行求值,变量的值在定义时就已经确定,后续不会因为其他变量的改变而改变)
引用变量:
$(变量名)
示例:

$@ 和 $^:
$@指代目标文件,$^指代依赖文件列表
即上面的内容可以改成:

这里$^会自动对应这个方法的依赖关系中的$(SRC),$@对应$(BIN)
4. 进阶3——多文件
4.1 获取当前目录下的所有文件
在Makefile中执行命令:$(shell 命令)
使用函数:$(函数名 参数1,参数2…)
获取当前目录下所有.c文件后缀的方法:
$(shell ls *.c),回顾:ls这个命令只列出名称$(wildcard *.c)
输入make运行结果:

执行的命令不回显
在命令前加:@

效果:

4.2 %<
当有多个源文件需要编译成目标文件,且每个源文件的编译命令基本相同时,使用 $< 能避免重复书写源文件名称。
$< 会被自动替换为当前规则中列出的第一个依赖文件的名称。例如,对于规则 target: dep1 dep2 dep3,在其对应的命令里使用 $<,它就代表 dep1
4.3 模式规则
%:通配符,如%.c:所有以.c结尾的文件
%.o: %.c :这个规则中,%所匹配的内容在目标和依赖中是相同的。(同名)
当 make工具处理这个规则时,会针对每个 .o 文件的构建分别执行一次规则里的命令
错误写法:

报错:
make: *** No targets. Stop.
因为:%.o: %.c只是一个模式规则,并没有具体构建目标。具体的构建目标是指在 Makefile 中明确指定要构建的文件,文件名需要是完整且能明确指向一个具体文件的。
正确使用示例:

额外语法:
- 命令可以多行,但是也要
Tab开头 - 注释用
#符号
理解执行过程:
- 具体构建目标:
code.exe,依赖所有.o文件,没有.o文件,于是到下一个规则%.o:%.c %.o:%.c规则中,make会针对每个.o文件分别执行一次命令,此时,每个.o文件变成了这个规则里的具体构建目标
运行结果:

🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!