目录
一. make/makefile 的概念
1.1 Makefile的基本语法
1.2 代码示例
二. make/makefile 的使用
2.1 文件的清理
2.2 make 的使用细节
2.2.1 make/make【目标名】
2.2.2 make 的执行规则
2.2.2.1 stat
2.3 makefile 的使用细节
2.3.1 .PHONY
2.3.2 makefile 的依赖关系推导
三. make/makefile的进阶使用
3.1 变量的定义和使用
3.1.1 变量实现代码的编译
3.1.2 特殊变量 $@ $^
3.2 多文件情况
3.2.1 版本1
3.2.2 版本2
一. make/makefile 的概念
make是 Linux 中的一个自动化构建工具,主要用于自动执行编译、链接等操作。你可以把它理解为:帮我们自动调用 gcc/g++,不用每次手动输入一大堆编译命令。
makefile是一个描述构建规则的文件,告诉 make:
哪些文件需要生成;
生成文件需要依赖哪些文件;
应该执行什么命令。
1.1 Makefile的基本语法
目标文件: 依赖文件1 依赖文件2 ...(依赖列表可为空) 依赖方法1 依赖方法2 ...myproc: myproc.cpp g++ myproc.cpp -o myproc myproc → 目标文件:最终要生成的文件 myproc.cpp → 依赖文件:生成目标所需要的文件 : → 依赖关系:表示 myproc 依赖 myproc.cpp g++ ... → 依赖方法:告诉 make 如何生成目标文件1.2 代码示例
# 使用相对路径 bin/myproc: src/myproc.cpp mkdir -p bin g++ -o bin/myproc src/myproc.cpp # 也可以使用绝对路径 # /home/lzx/myproc: /home/lzx/code/myproc.cpp # g++ -o /home/lzx/myproc /home/lzx/code/myproc.cpp# 默认读取当前目录中的 Makefile make # -f:指定 Makefile 文件路径(不切换工作目录) make -f /home/lzx/project/Makefile # -C:指定工作目录(切换目录后执行 make) make -C /home/lzx/project # -C 和 -f 组合使用 make -C /home/lzx/project -f Makefile二. make/makefile 的使用
2.1 文件的清理
当我们完成编译想要清除编译好的可执行文件时,可以在makefile输入以下指令:
2.2 make 的使用细节
2.2.1 make/make【目标名】
make默认选择 Makefile 中的第一个目标进行构建,但我们也可以通过make【目标名】指定其他目标。
#Makefile myproc: myproc.cpp g++ myproc.cpp -o myproc test: test.cpp g++ test.cpp -o test hello: hello.cpp g++ hello.cpp -o hellomake # 默认构建第一个目标 myproc make test # 指定构建 test make hello # 指定构建 hello2.2.2 make 的执行规则
make也并不是一定会对目标进行构建,为什么呢:
因为 make的作用不仅是执行编译命令,还会根据目标文件和依赖文件的时间戳,判断是否需要重新构建。
myproc: myproc.cpp g++ myproc.cpp -o myproc情况 | make 的行为 |
|---|---|
| 执行命令,生成目标文件 |
| 重新编译,更新目标文件 |
| 不重新编译,认为目标已经是最新的 |
2.2.2.1 stat
我们之前提到 make 会根据目标文件和依赖文件的时间戳来判断是否构建,指令stat可以让我们更直观的理解。
stat是 Linux 中用于查看文件或目录详细属性信息的命令。
时间 | 含义 | 什么情况下会变化? |
|---|---|---|
Access(atime) | 最近一次访问文件内容的时间 | 读取文件内容时,时间可能更新 |
Modify(mtime) | 文件内容最后修改的时间 | 修改文件内容时更新 |
Change(ctime) | 文件状态最后变化的时间 | 修改权限、所有者、硬链接数等信息时更新;修改文件内容也会更新 |
Birth | 文件创建时间 | 表示文件创建的时间,前提是文件系统支持记录 |
make 正是基于目标文件、依赖文件的修改时间(Modify)来判断的。
2.3 makefile 的使用细节
2.3.1 .PHONY
.PHONY 用于声明伪目标,告诉make,这个目标不是一个真正需要生成的文件,而是一个需要执行的任务。
我们在之前提到了make的执行规则:要判断执行文件和目标文件的时间戳,而.PHONY 的作用就是告诉 make,指定的目标是伪目标,不对应一个需要生成的实际文件,因此不需要通过检查同名目标文件的时间戳来判断是否执行命令。
2.3.2 makefile 的依赖关系推导
make从上往下寻找目标的依赖关系,找到最底层的依赖后,再从下往上依次执行构建命令。
#Makefile 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① 从上往下查找依赖(入栈):
myproc ↓ myproc.o ↓ myproc.s ↓ myproc.i ↓ myproc.c(源文件已存在)② 从下往上构建目标(出栈):
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/makefile的进阶使用
3.1 变量的定义和使用
Makefile 中可以使用=定义变量:
通过$(变量名)引用变量。
我们会发现每行命令前面都有符号@,@的作用主要是取消命令的回显,如果不加@结果如下:
3.1.1 变量实现代码的编译
3.1.2 特殊变量 $@ $^
$@和$^都是Makefile 中的自动变量,用于简化构建命令。
自动变量 | 含义 |
|---|---|
| 当前规则的目标文件 |
| 当前规则的所有依赖文件(去重) |
3.2 多文件情况
3.2.1 版本1
BIN = myproc CC = g++ OBJ = myproc.o FLAGS = -c # 1. 默认目标是 myproc,依赖 myproc.o $(BIN): $(OBJ) @$(CC) $^ -o $@ # 2. % 是模式匹配符,用于匹配文件名中的相同部分 # 例如:make 需要生成 myproc.o # %.o → myproc.o,此时 % 匹配到 myproc # %.cpp → myproc.cpp,将 myproc 代入依赖文件名 # 因此推导出:myproc.o 依赖 myproc.cpp %.o: %.cpp @$(CC) $(FLAGS) $< -o $@ # 3. $< 是自动变量,表示当前规则的第一个依赖文件 # 这里 $< → myproc.cpp # 而 $@ → myproc.o,表示当前目标文件 # 所以上面的命令展开后为: # g++ -c myproc.cpp -o myproc.o
%:模式匹配符,根据目标文件名匹配相同部分,推导出依赖文件名。
$<:自动变量,表示当前规则的第一个依赖文件。
$@:自动变量,表示当前规则的目标文件。
3.2.2 版本2
# 1. 定义变量 BIN = myproc # 最终生成的可执行文件 CC = g++ # 编译器 SRC = $(shell ls *.cpp) # 获取当前目录下所有 .cpp 文件 # SRC = $(wildcard *.cpp) # 另一种写法,更推荐 OBJ = $(SRC:.cpp=.o) # 将所有 .cpp 后缀替换为 .o LFLAGS = -o # 指定输出文件名 FLAGS = -c # 只编译生成 .o,不链接 RM = rm -f # 删除命令 # 2. 定义最终目标及其依赖关系 # $(BIN) → myproc # $(OBJ) → main.o add.o sub.o(假设存在这三个源文件) # 表示 myproc 依赖这些 .o 文件 $(BIN): $(OBJ) @$(CC) $^ $(LFLAGS) $@ @echo "linking... $^ to $@" # $^ → 所有依赖文件,即 main.o add.o sub.o # $@ → 当前目标文件,即 myproc # 实际执行:g++ main.o add.o sub.o -o myproc # 3. 定义模式规则,分别生成 .o 文件 # % 是模式匹配符 # 需要生成 main.o 时,% 匹配 main,推导出 main.cpp # 需要生成 add.o 时,% 匹配 add,推导出 add.cpp # 每次匹配都对应一条独立的编译规则 %.o: %.cpp @$(CC) $(FLAGS) $< @echo "compiling... $< to $@" # $< → 当前规则的第一个依赖文件,例如 main.cpp # $@ → 当前规则的目标文件,例如 main.o # 实际执行:g++ -c main.cpp # 默认生成 main.o # @ 表示不回显命令本身1.
$( ):引用变量或调用函数
引用变量:
$(CC)获取变量CC的值。调用函数:例如
$(wildcard *.cpp)。示例:
CC = g++,那么$(CC)就会展开为g++。
2.
wildcard:使用 Makefile 自带的功能查找文件
示例:
SRC = $(wildcard *.cpp)。
*.cpp表示匹配所有以.cpp结尾的文件。查找结果会赋值给变量
SRC。按照
make的工作目录查找,而不是 Makefile 所在的目录。
3.
shell:执行 Shell 命令并获取输出
示例:
SRC = $(shell ls *.cpp)。
$(shell ...)会执行括号中的 Shell 命令,并获取其标准输出。
ls *.cpp用于列出匹配的.cpp文件,结果赋值给SRC。同样以
make的工作目录为基