RISC-V Binutils工具链17之make命令的使用
Linux ELF 分析17之make工具的使用
目录
在 C/C++ 等编译型语言的开发中,一个稍微复杂点的项目可能包含成百上千个源文件,如果每次修改了一行代码,你都要手动去敲几十条 gcc 命令来重新编译和链接,那绝对是一场灾难。
make 就是为了解决这个问题而生的,主要有如下三个作用:
- 自动化执行编译命令,需配合Makefile使用
- 增量编译
- 维护复杂的依赖关系
1 make 工具常用选项
$ make --help
输出log见:make_help
1.1 帮助与版本
| 选项 | 长选项 | 说明 |
|---|---|---|
| -h | --help | 显示帮助信息并退出 |
| -v | --version | 打印 make 版本信息并退出 |
1.2 目录与环境控制
| 选项 | 长选项 | 说明 |
|---|---|---|
| -C DIRECTORY | --directory=DIRECTORY | 在执行前切换到指定目录,常用于子项目构建 |
| -w | --print-directory | 每次进入目录时打印当前路径(常与 -C 配合) |
| --no-print-directory | 禁用 -w,即使隐式启用也关闭 |
1.3 Makefile 输入控制
| 选项 | 长选项 | 说明 |
|---|---|---|
| -f FILE | --file=FILE, --makefile=FILE | 指定使用的 Makefile 文件(可多次使用) |
| -I DIRECTORY | --include-dir=DIRECTORY | 添加包含目录,用于 include 指令查找 Makefile 片段 |
| -E STRING | --eval=STRING | 将 STRING 作为 Makefile 语句直接求值(类似内联定义) |
| -r | --no-builtin-rules | 禁用所有内置的隐式规则(如 .c.o:) |
| -R | --no-builtin-variables | 禁用所有内置变量定义 |
1.4 目标构建行为控制
| 选项 | 长选项 | 说明 |
|---|---|---|
| -B | --always-make | 无条件重新构建所有目标(忽略时间戳) |
| -n | --just-print, --dry-run, --recon |
只打印命令,不执行(模拟运行) |
| -s | --silent, --quiet |
不回显(不打印)执行的命令 |
| --no-silent | 启用命令回显(取消 --silent) |
|
| -t | --touch | 不执行命令,而是 touch 目标文件(更新时间戳) |
| -q | --question | 不运行任何命令,仅通过退出码判断目标是否已是最新的(0=是,2=否) |
| -W FILE | --what-if=FILE, --new-file=FILE, --assume-new=FILE | 假设 FILE 是最新的 |
| -o FILE | --old-file=FILE, --assume-old=FILE | 假设 FILE 是极旧的,且不重新生成它 |
1.5 并行与性能控制
| 选项 | 长选项 | 说明 |
|---|---|---|
| -j [N] | --jobs[=N] | 允许同时运行 N 个作业(任务)无参数时为无限并行 |
| -l [N] | --load-average[=N], --max-load[=N] | 当系统负载 ≥ N 时,不再启动新 job |
| -O[TYPE] | --output-sync[=TYPE] | 同步并行 job 的输出,避免混杂 类型: target, line, recurse |
使用make -j 还是make -j$(nproc)?
推荐使用 make -j$(nproc),而不是简单的 make -j,其区别如下:
| 对比项 | make -j |
make -j$(nproc) |
|---|---|---|
| 并行度 | 无限,make 会尽可能多地启动任务(无限制) |
根据 CPU 核心数自动设置并行任务数 |
| 安全性 | 极低 | 高 |
| 效率 | 可能崩溃 | 最大化利用资源 |
1.6 错误与容错控制
| 选项 | 长选项 | 说明 |
|---|---|---|
| -i | --ignore-errors | 忽略命令执行错误,继续构建 |
| -k | --keep-going | 即使某些目标失败,也尽可能继续构建其他目标 |
| -S | --no-keep-going, --stop | 取消 -k 行为,遇到失败立即停止 |
尽量多的构建:
比如某个工程有许多错误,默认是发现一处错误就停止构建,有时想尽量一次暴露所有错误,然后修正,可以使用如下命令:
$ make -k # 尽可能完成所有可构建目标,从而方便修复所有错误
1.7 调试与诊断选项
| 选项 | 长选项 | 说明 |
|---|---|---|
| -d | 打印大量调试信息(包括依赖关系、决策过程) | |
| --debug[=FLAGS] | 更细粒度的调试输出 常见标志: a(all), b(basic), m(makefile), v(verbose) |
|
| -p | --print-data-base | 打印 make 内部数据库(所有变量、规则、目标) |
| --trace | 打印每个目标的构建原因和时间戳信息 | |
| --warn-undefined-variables | 当引用未定义变量时发出警告 |
如何查看 Makefile 的 include build.mk 依赖?
如果某个系统的构建比较复杂,我们有时需要梳理Makefile,mk文件之间的关系,可以使用 make 命令本身提供的选项来查看其解析 Makefile 的过程:
方法1:make -d:会输出 make 执行过程中的所有调试信息,包括:
-
解析了哪些 Makefile。
-
include了哪些文件(成功或失败)。 -
变量的展开过程。
-
目标是否需要重建。
-
执行的命令。
方法2:make -p:打印出 make 读取的所有 Makefile 的内容(包括内置规则和你 include 的文件),以及所有的规则、变量和隐含规则。
构建过程与编译过程有不同的问题及定位问题的方法:
| 问题 | 工具 | 正确方法 |
|---|---|---|
查看 include build.mk 是否被处理 |
make | 使用 make -d 或 make -p |
| 查看编译器详细信息(头文件搜索路径等) | riscv64-unknown-linux-gnu-gcc | 使用 -v |
2 典型使用场景
| 场景 | 推荐命令 |
|---|---|
| 查看会执行什么命令 | make -n |
| 不实际构建,检查是否最新 | make -q |
| 构建时显示目录切换 | make -w |
| 多核并行加速构建 | make -j$(nproc) |
| 忽略错误继续构建 | make -k |
| 调试 Makefile 逻辑 | make -d 或 make --debug=m |
| 查看所有变量和规则 | make -p |
| 强制重新构建所有 | make -B |
| 假设某文件已修改 | make -W file.c |

浙公网安备 33010602011771号