Makefile 不只是编译用的
在项目根目录放一个 Makefile,可以把 pytest、ruff、docker compose 这类零散命令统一成 make <target>。
很多人只在编译项目时见过 make test、make build、make clean。但 Make 更常见的价值,是把一堆脚本收成可重复执行的工作流。你不需要反复记 python xxx.py、bash scripts/deploy.sh、docker compose up -d 的细节,只需要记住一个入口:make <target>。
先说清楚:Make 是什么
Make 是一个“按目标执行命令”的工具。你先定义目标,比如 test、lint、run、backup,再告诉它每个目标该做什么。之后执行时,只要敲:
1 | |
它就会按你写好的步骤运行。
这件事看起来像“给 shell 脚本起别名”,但它至少有两个直接的好处:
- 入口统一:团队成员执行同一套命令。
- 依赖清楚:一个目标可以依赖另一个目标,不用手工串流程。
最常见的用法:把杂乱命令收口
很多项目的问题不是没有脚本,而是脚本太散。
一个普通项目里,常见动作通常包括:
- 安装依赖
- 启动开发环境
- 跑测试
- 格式化代码
- 打包
- 部署前检查
这些命令往往分散在 README、历史命令和聊天记录里。时间一长,就很容易忘。
这时可以先写一个最小版本的 Makefile:
makefile
.PHONY: install test lint format run
install:
pip install -r requirements.txt
test:
pytest
lint:
ruff check .
format:
ruff format .
run:
python app.py
这里的 .PHONY 可以理解为:这些目标不是文件名,而是动作名。
之后只要记:
1 | |
比起翻 README 找命令,这种方式更稳定。
再进一步:把流程串起来
Make 的另一层价值,是把动作连成流程。
如果你不想每次发布前都手动回忆“先格式化、再测试、最后打包”,可以直接写成:
makefile
.PHONY: check build release
check: format lint test
build:
python -m build
release: check build
@echo “ready to release”
现在执行:
1 | |
它会先跑 check,而 check 又会依次触发 format、lint、test。
这不是复杂技巧,只是把顺序写进文件。以后谁来执行,流程都一致。
关于重复执行:先把边界写清楚
自动化常见的问题不是“写不出来”,而是“第二次运行会不会出问题”。
例如目录已存在、文件重复生成、旧产物没清理、环境状态不一致。Make 本身不能替你解决幂等性,也就是“多跑几次结果是否一致”,但它能促使你把边界写清楚。
例如:
makefile
.PHONY: init clean
init:
mkdir -p data logs tmp
clean:
rm -rf build dist .pytest_cache
这里 mkdir -p 的意思是:目录已存在时不报错。自动化关注的不只是“第一次成功”,还包括“之后还能重复执行”。
如果某个目标会修改状态,名字最好更具体,比如 bootstrap、reset-db、seed,不要都叫 run。名字越具体,误操作越少。
Makefile 适合什么,不适合什么
适合的场景:
- 本地开发命令很多,记不住
- 想给项目提供统一入口
- 想把检查、测试、打包串成固定顺序
- 想让 CI 和本地执行同一套动作
不太适合的场景:
- 逻辑分支特别复杂
- 需要大量循环、判断、错误处理
- 跨平台差异很多,尤其是 shell 行为不一致时
Makefile 更适合做“调度层”,不适合承载太多业务逻辑。复杂逻辑可以放进 scripts/*.sh 或 scripts/*.py,Make 只负责调用。
一个常见写法是:
makefile
.PHONY: sync deploy
sync:
bash scripts/sync.sh
deploy:
python scripts/deploy.py
这样 Makefile 更短,脚本也更容易单独测试。
日常任务也可以用 Makefile
即使没有编译需求,也可以给日常任务加一个 Makefile,比如:
makefile
.PHONY: note backup preview
note:
python scripts/new_note.py
backup:
rsync -av ./docs/ /path/to/backup/
preview:
hexo server
之后直接执行:
1 | |
不必再翻历史记录找备份命令。
这类自动化的价值不在技术炫耀,而在减少重复决策:少记几条命令,少翻一次记录,少跑错一次参数。
一个很实用的下一步
Makefile 的价值还在于可见性。项目根目录放一个 Makefile,别人一打开就能知道这个项目平时怎么运行:make help、make test、make release。
如果准备继续整理工作流,可以加一个 help 目标,把常用命令打印出来。这样比继续堆更多 target 更实用。