Makefile 不只是编译用的

make deploy 可以把 npm run build && rsync ... && ssh ... 这类长命令收成一个稳定入口。

很多人第一次接触 Make,都是在 C/C++ 项目里编译代码,于是很容易把它理解成“只适合编译的老工具”。这个判断不算错,但不完整。对日常项目来说,Makefile 更像一个命令调度入口:把会重复执行、顺序固定、参数容易忘的命令,收成几个短名字。

它解决的不是“能不能写复杂脚本”,而是另一个更常见的问题:工作流散落在 README、shell 历史记录、编辑器任务和 CI 配置里,真正执行时还得自己临场拼接。

Make 最适合做什么

Make 的核心不是“编译”,而是“目标”和“依赖”。

  • 目标:你想做的事,比如 buildtestdeploy
  • 依赖:做这件事之前,哪些事得先做

如果你平时会写这些命令:

  • 启动本地开发环境
  • 格式化代码
  • 跑测试
  • 打包静态文件
  • 同步配置
  • 生成文档
  • 一键部署

那 Makefile 通常就有用。

它不一定比 shell 脚本更强,但通常比“十几个脚本文件 + 多套入口”更收敛。它的优点是轻、直观、系统里常见;边界是复杂逻辑不好写,跨平台细节也要小心。

一个够用的 Makefile

下面这个例子可以直接作为起点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.PHONY: help dev fmt test build deploy clean

help:
@echo "make dev # 启动开发环境"
@echo "make fmt # 格式化"
@echo "make test # 跑测试"
@echo "make build # 构建产物"
@echo "make deploy # 部署"
@echo "make clean # 清理文件"

dev:
npm install
npm run dev

fmt:
npm run format

test:
npm test

build:
npm run build

deploy: test build
rsync -avz dist/ <user>@<host>:/var/www/project/

clean:
rm -rf dist

有三个点值得记:

1. .PHONY 要写

它的意思是:这些目标不是实际文件。
不写也能跑,但一旦目录里刚好有个同名文件,比如 build,Make 可能会误判“这个目标已经存在,不用执行了”。

2. 依赖比注释更可靠

deploy: test build 的意思是,部署前先测试、再构建。
这比在文档里写“部署前请先执行测试”更可靠,因为文档会被忘,依赖会被执行。

3. 入口统一,切换成本会下降

团队协作里常见的问题不是不会写命令,而是每个人都记一套命令。
有人执行 pnpm build,有人跑包装脚本,有人手动上传。最后“怎么部署”只能靠聊天记录和口口相传。Makefile 的价值就在这:把入口收成 make xxx

不只管代码,也可以管环境

Makefile 还有一个实用场景:检查运行环境。

1
2
3
4
5
6
7
8
9
10
11
.PHONY: info check

info:
uname -a
lscpu || true
free -h || true

check:
command -v node
command -v rsync
command -v ssh

这里的思路很简单:

  • make info:快速看系统和硬件信息
  • make check:确认依赖命令是否存在

|| true 的意思是:某个命令不存在时,不要让整个流程直接报错退出。适合做“尽量多输出信息”的检查。

这类目标适合放在项目根目录。新机器、新环境、迁移服务器时,先跑一遍,通常比手工核对更省事。

用变量收住那些容易改的地方

不要把路径、主机名、端口直接写死在命令里。Makefile 虽然简单,但变量够用。

1
2
3
4
5
6
APP=project
HOST=<host>
REMOTE_DIR=/var/www/$(APP)

deploy:
rsync -avz dist/ <user>@$(HOST):$(REMOTE_DIR)/

如果想临时覆盖:

1
make deploy HOST=<staging-host>

这比复制一份 deploy-staging.shdeploy-prod.sh 干净,也更容易看清差异到底在哪:通常只是变量不同,不是流程不同。

什么时候别硬上 Make

Make 不是工作流平台,别把它写成一门新语言。

下面这些情况,更适合换工具或下沉到脚本里:

1. 逻辑分支很多

比如十几个参数组合、复杂条件判断、循环重试。
这种时候 shell、Python、Node 脚本会更清楚。

2. 强依赖跨平台一致性

Windows、macOS、Linux 的命令差异不少。
如果使用者环境很杂,Makefile 可能反而会变成兼容性问题入口。

3. 需要详细交互

Make 更适合“执行一组命令”,不适合做交互式向导。
它应该是薄入口,不该承担全部业务逻辑。

一个实用做法是:Make 负责调度,脚本负责复杂实现

1
2
3
4
5
backup:
bash scripts/backup.sh

publish:
python3 scripts/publish.py

这样既保留统一入口,也不会把 Makefile 写成难读的迷宫。

写 Makefile 时顺手整理动作边界

写 Makefile 的过程,其实是在回答几个问题:

  • 这个项目最常用的动作有哪些?
  • 哪些步骤应该强制串联?
  • 哪些参数应该显式暴露?
  • 哪些检查应该自动做,而不是靠人记住?

这些问题本来就存在。Makefile 只是把它们从聊天记录、历史命令和个人习惯里拎出来,变成能执行的入口。

如果你手边已经有一堆“偶尔执行一次,但每次都要翻记录”的命令,先写一个 Makefile,放进 5 个最常用目标,通常就已经够用了。


Makefile 不只是编译用的
https://ghost.kasumi.live/2026/03/17/Makefile 不只是编译用的/
作者
Amadeus
发布于
2026年3月17日
许可协议