Makefile 快速入门
Makefile 通过定义任务规则来自动化构建流程,很多开源项目都用它管理构建、测试、部署等操作。本文只讲开发者需要掌握的部分。
什么是 Makefile
Makefile 是 Make 构建工具的配置文件,用简洁的语法定义任务和依赖关系。虽然它最初是为 C/C++ 编译设计的,但现在广泛用于自动化各种开发任务——构建、测试、部署、代码生成等。
为什么用 Makefile 而不是写 shell 脚本?
- 语法简洁,一个命令就能跑起一串复杂操作
- 基于文件修改时间自动判断是否需要重新构建
make命令自带--help感觉(make无参数时打印目标列表)- 几乎所有 Linux/macOS 系统自带
make,不需要额外安装
基本语法
一个 Makefile 由**规则(Rule)**组成,每个规则的结构:
text
目标: 依赖
命令
- 目标(Target):任务名称,
make 目标名时执行 - 依赖(Prerequisites):执行前需要检查的条件(文件或其他目标)
- 命令(Recipe):要执行的 shell 命令
- 缩进必须用 Tab,不能用空格——这是最常见的错误
第一个 Makefile
makefile
# 构建
build:
gcc main.c -o app
# 运行
run: build
./app
# 清理
clean:
rm -f app
使用:
bash
make build # 编译
make run # 编译并运行(run 依赖 build)
make clean # 清理
实际项目中的 Makefile
以下是 Web/Node.js 项目中常见的 Makefile:
makefile
.PHONY: dev build test lint clean install help
help: ## 显示帮助信息
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
install: ## 安装依赖
npm install
dev: ## 启动开发服务器
npm run dev
build: ## 构建生产版本
npm run build
test: ## 运行测试
npm run test
lint: ## 代码检查
npm run lint
clean: ## 清理构建产物
rm -rf dist node_modules
.PHONY 声明这些目标不是真实文件——即使目录下存在同名文件,make 也会执行。
help 目标利用注释自动生成帮助信息:
bash
$ make help
dev 启动开发服务器
build 构建生产版本
test 运行测试
lint 代码检查
clean 清理构建产物
install 安装依赖
help 显示帮助信息
变量
makefile
# 定义变量
APP_NAME = my-app
BUILD_DIR = dist
NODE = node
# 使用变量(用 $() 引用)
build:
$(NODE) build.js --output $(BUILD_DIR)
# 环境变量
PORT = 3000
start:
PORT=$(PORT) npm start
也可以在命令行中覆盖变量:
bash
make build NODE=node18
常用模式
带参数的任务
makefile
.PHONY: create-component
COMPONENT ?= Button
create-component: ## 创建组件模板 (用法: make create-component COMPONENT=Modal)
mkdir -p src/components/$(COMPONENT)
touch src/components/$(COMPONENT)/index.tsx
touch src/components/$(COMPONENT)/style.module.css
bash
make create-component COMPONENT=Modal
多目标并行
makefile
.PHONY: fmt lint test
check: fmt lint test ## 运行所有检查
fmt: ## 格式化代码
npx prettier --write .
lint: ## 代码检查
npx eslint .
test: ## 运行测试
npx vitest
make check 会依次执行 fmt、lint、test。如果中间某一步失败,后续步骤不会执行。
条件判断
makefile
ENV ?= development
run:
ifeq ($(ENV), production)
node dist/app.js
else
npm run dev
endif
常见错误
1. 用空格代替 Tab
text
# 错误
build:
gcc main.c -o app # 这里是 4 个空格,会报错
# 正确
build:
gcc main.c -o app # 这里是 Tab
如果编辑器自动把 Tab 转成空格,在 Makefile 开头加:
makefile
.RECIPEPREFIX = >
build:
>gcc main.c -o app
2. 目标名和文件名冲突
如果目录下有一个叫 clean 的文件,make clean 会提示"clean is up to date"而不执行。用 .PHONY 声明即可解决。
3. 命令中的 $ 符号
shell 变量需要用 $$ 转义:
makefile
print-time:
echo "Current time: $$(date)"
速查表
text
基本语法:
target: prereq 目标 + 依赖
command 命令(必须 Tab 缩进)
.PHONY: target 声明非文件目标
变量:
VAR = value 定义变量
$(VAR) 引用变量
VAR ?= default 设置默认值
make target VAR=val 命令行覆盖
常用模式:
make 执行默认目标(第一个)
make target 执行指定目标
make -j 并行执行
make -n 预览(不实际执行)
make -f custom.mk 指定 Makefile 文件
常见错误:
缩进用空格而非 Tab
目标名和文件名冲突(用 .PHONY)
Shell 变量 $ 要写成 $$