【Linux】make & Makefile 核心知识点

make 是一个构建工具;Makefile 是它读取的规则脚本,用来自动化编译、链接工程,避免重复敲长串 gcc 命令,解决依赖更新只重编改动文件。

一、基础原理

make 核心思想:时间戳比较

  • 目标文件(target)和依赖文件(prerequisites)的修改时间对比
  • 如果:目标不存在,或者依赖比目标新 → 执行命令(recipe)
  • 如果:目标存在且所有依赖都比它旧 → 跳过,不执行
bash 复制代码
# 语法模板
target : prerequisites
	command

command 前面必须是 Tab,不能是空格!

示例:

bash 复制代码
main: main.c hello.c
	gcc main.c hello.c -o main

执行:

bash 复制代码
make
# 指定目标
make main
# 清理
make clean

target(目标)& prerequisites(依赖)

一条 Makefile 规则的标准格式:

bash 复制代码
target : prerequisites
	command

中文:目标:依赖

后面的 command 叫做命令

  1. target 目标

目标:这条规则最终要生成的东西(或者一个动作),分两大类:

  1. 文件目标(普通目标) :是磁盘上真实存在的文件
    • 例子:main、main.o、hello.o
    • make 判断逻辑:对比这个文件的时间戳
  2. 伪目标(.PHONY) :不是文件,只是一个标签,代表一个动作
    • 例子:clean、all、install
    • 特点:不管有没有同名文件,只要被指定就执行命令

✅ 一句话:target 是"我想要得到什么"

bash 复制代码
main: main.o hello.o
# target = main,我们最终要生成 main 可执行文件
  1. prerequisites 依赖(前置条件)

依赖:生成 target 需要的原料、前提文件,可以是多个,空格隔开;依赖本身也可以是另一条规则的 target。

复制代码
main: main.o hello.o
# prerequisites = main.o hello.o
# 想要得到 main,必须先有 main.o 和 hello.o

make 会递归检查依赖 :如果 main.o 不存在或过时 → 先去找生成 main.o 的规则,先生成 main.o

✅ 一句话:prerequisites 是"生成目标需要依靠哪些东西"

  1. make 的核心判断逻辑(时间戳规则)

对于文件类型的 target:

如果满足下面任意一条 → 执行 command:① target 文件不存在;② 任意一个 prerequisites 的修改时间晚于 target 的修改时间(原料更新了,产物过时)
如果 target 存在,并且所有依赖都比它旧 → 跳过这条规则,不执行命令

举例子:

复制代码
main: main.o hello.o
	gcc main.o hello.o -o main
  • 改了 main.c → main.o 更新 → main.o 比 main 新 → 重新链接 main
  • 只改注释,但是没重新编译 main.o → main.o 时间没变 → 不重新链接
  • 删掉 main → target 不存在 → 执行链接命令

伪目标不受时间戳判断!伪目标只要被选,就一定执行。

完整的 Makefile 示例(包含 main、main.o、hello.o 三条规则):

bash 复制代码
main: main.o hello.o
	gcc $^ -o $@
main.o: main.c
gcc -c main.c
hello.o: hello.c
gcc -c hello.c

依赖树:

复制代码
main
├─ main.o
│  └─ main.c
└─ hello.o
   └─ hello.c

执行 make main,make 自底向上检查。

  1. 容易踩坑:头文件默认不会被当成依赖
bash 复制代码
main.o: main.c
	gcc -c main.c

这里 prerequisites 只有 main.c,没有 main.h

你修改 main.h,make 看不到这个依赖,不会重新编译 main.o! 解决方案:-MMD 自动生成 .d 依赖文件,把头文件加入依赖列表。

  1. 自动变量和它们的对应关系(绑定 target /prerequisites)

假设规则:main: main.o hello.o

表格

变量 对应
$@ target → main
$< 第一个 prerequisite → main.o
$^ 所有 prerequisites(去重)→ main.o hello.o
$? 比 target 更新的那些 prerequisites
target(目标) prerequisites(依赖)
含义 产出物 / 动作标签 原料、前置条件
文件目标 真实文件 源文件、中间 .o 文件
伪目标 clean/all 一般没有依赖,或者依赖其他目标
make 判断 检查自己是否存在 / 是否过时 和目标对比时间戳

二、Makefile 四大组成

  1. 显式规则:上面这种,手写目标、依赖、命令
  2. 隐晦规则(内置隐式规则) :make 自带,比如 .c 文件可以自动用 gcc -c 生成 .o 例:写 main.o: main.c,不用写 gcc -c,make 自动补
  3. 变量定义
  4. 文件指示 + 注释 :# 注释;include 引入其他 Makefile

三、变量(非常常用)

1. 变量赋值四种方式

bash 复制代码
# = 递归赋值:展开时才取值,会向后找变量,容易循环
VAR = abc
:= 简单赋值:定义时立刻展开(推荐!)
VAR := abc
?= 条件赋值:变量未定义才赋值
VAR ?= abc
+= 追加赋值
VAR += def

2. 自动变量

表格

变量 含义
$@ 当前规则的目标
$< 第一个依赖文件
$^ 所有依赖文件(去重)
$? 比目标新的依赖文件
$+ 所有依赖文件(不去重)

示例:

bash 复制代码
main: main.o hello.o
	gcc $^ -o $@
main.o: main.c
gcc -c $< -o $@
hello.o: hello.c
gcc -c $< -o $@

四、伪目标 .PHONY

问题:如果目录里刚好有名为 clean 的文件,make clean 会判断:clean 文件存在,不执行清理命令! 解决:.PHONY 声明伪目标,告诉 make:这不是生成文件,只是一个动作,永远执行。

复制代码
.PHONY: clean all
all: main
main: main.o hello.o
gcc $^ -o $@
clean:
rm -f *.o main

常用伪目标:all(编译全部)、clean、install、test

伪目标(Phony Target)

一句话:伪目标是一个标签,不是磁盘上的文件,作用只是用来执行一组命令。

普通目标:main、main.o → 对应真实文件,make 靠时间戳 判断要不要执行。 伪目标:clean、all、install、test → 不走时间戳判断,只要被指定就执行。

语法:.PHONY

.PHONY 是 make 的特殊内置目标,把它的依赖声明成伪目标:

bash 复制代码
.PHONY: clean all

写在这一行后面的名字,全部标记为伪目标。

示例:

bash 复制代码
.PHONY: clean
clean:
	rm -f *.o main

如果不写 .PHONY: clean:

  • 当目录里存在一个名叫 clean 的文件 / 文件夹
  • clean 这个目标没有依赖,make 认为:目标已经存在、最新,输出 make: 'clean' is up to date.,不会执行删除命令

加上 .PHONY 之后: make 直接忽略同名文件,无条件执行 clean 的命令,不再对比时间戳。

普通目标(文件目标) 伪目标
本质 要生成的文件 动作标签,不生成文件
判断规则 比较 target 和 prerequisites 的时间戳 不看时间戳,无条件执行
声明 不需要 .PHONY 需要加到 .PHONY: 后面
例子 main、main.o clean、all、install、run

例子 main、main.o clean、all、install、run

相关推荐
不吃鱼的羊1 小时前
如何建立高自尊
java·服务器·前端
Zevalin爱灰灰1 小时前
Linux入门第一部分(Ubuntu入门篇) 第三章 shell脚本入门(一)
linux·ubuntu·嵌入式
丘山望岳1 小时前
Linux基础指令入门篇
linux·运维·服务器
笨笨饿1 小时前
#143_屏幕画撕裂的问题及解决方法
linux·stm32·单片机·嵌入式硬件·mcu·lvgl·嵌入式实时数据库
想做小南娘,发现自己是女生喵1 小时前
LAN Device Monitor 第二周学习文档
java·服务器·数据库
小年糕是糕手1 小时前
【AI】用 Python 做一个 AI 截图检索助手:把收藏夹里的截图找回来
linux·人工智能·python·chatgpt·agent·codex·deepseek
hhhjhl1 小时前
Zod 跨端三方库仓颉适配全流程
linux·运维·ubuntu
翔云1234562 小时前
Gaea(小米MySQL Proxy)整体架构
运维·数据库·mysql
程序员清风2 小时前
LibreChat 实战:用 Docker 搭建可扩展的多模型对话平台
运维·docker·容器