makefile

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Makefile Skill

Makefile 技能指南

Guidance for creating and maintaining GNU Make build automation.
本文为创建和维护GNU Make构建自动化提供指导。

Quick Navigation

快速导航

TopicReference
Rules, prerequisites, targetssyntax.md
Variable types and assignmentvariables.md
Built-in functionsfunctions.md
Special and phony targetstargets.md
Recipe execution, parallelrecipes.md
Implicit and pattern rulesimplicit.md
Common practical patternspatterns.md

主题参考文档
规则、依赖项、目标syntax.md
变量类型与赋值variables.md
内置函数functions.md
特殊与伪目标targets.md
脚本执行、并行构建recipes.md
隐式与模式规则implicit.md
常见实用模式patterns.md

Core Concepts

核心概念

Rule Structure

规则结构

makefile
target: prerequisites
        recipe
Critical: Recipe lines MUST start with TAB character.
makefile
target: prerequisites
        recipe
重要提示: 脚本行必须以TAB字符开头。

File vs Phony Targets

文件目标与伪目标

makefile
undefined
makefile
undefined

File target - creates/updates a file

文件目标 - 创建/更新文件

build/app.o: src/app.c $(CC) -c $< -o $@
build/app.o: src/app.c $(CC) -c $< -o $@

Phony target - action, not a file

伪目标 - 执行操作,而非生成文件

.PHONY: clean test install
clean: rm -rf build/
undefined
.PHONY: clean test install
clean: rm -rf build/
undefined

Variable Assignment

变量赋值

OperatorNameWhen Expanded
:=
SimpleOnce, at definition
?=
ConditionalIf not already set
=
RecursiveEach use (late binding)
+=
AppendAdds to existing value
makefile
CC := gcc              # Immediate
CFLAGS ?= -O2          # Default, overridable
DEBUG = $(VERBOSE)     # Late binding
CFLAGS += -Wall        # Append
运算符名称展开时机
:=
简单赋值定义时一次性展开
?=
条件赋值仅当变量未设置时赋值
=
递归赋值每次使用时展开(延迟绑定)
+=
追加赋值向现有值添加内容
makefile
CC := gcc              # 立即展开
CFLAGS ?= -O2          # 默认值,可被覆盖
DEBUG = $(VERBOSE)     # 延迟绑定
CFLAGS += -Wall        # 追加内容

Automatic Variables

自动变量

VariableMeaning
$@
Target
$<
First prerequisite
$^
All prerequisites (unique)
$?
Prerequisites newer than target
$*
Stem in pattern rules

变量含义
$@
目标文件
$<
第一个依赖文件
$^
所有依赖文件(去重)
$?
比目标文件新的依赖文件
$*
模式规则中的匹配部分

Essential Patterns

实用模式

Self-Documenting Help

自文档化帮助

makefile
.DEFAULT_GOAL := help

help: ## Show available targets
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
		awk 'BEGIN {FS = ":.*?## "}; {printf "  %-15s %s\n", $$1, $$2}'

install: ## Install dependencies
	uv sync

test: ## Run tests
	uv run pytest
makefile
.DEFAULT_GOAL := help

help: ## 显示可用目标
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
		awk 'BEGIN {FS = ":.*?## "}; {printf "  %-15s %s\n", $$1, $$2}'

install: ## 安装依赖
	uv sync

test: ## 运行测试
	uv run pytest

Platform Detection

平台检测

makefile
UNAME_S := $(shell uname -s)

ifeq ($(UNAME_S),Darwin)
    OPEN := open
else ifeq ($(UNAME_S),Linux)
    OPEN := xdg-open
endif
makefile
UNAME_S := $(shell uname -s)

ifeq ($(UNAME_S),Darwin)
    OPEN := open
else ifeq ($(UNAME_S),Linux)
    OPEN := xdg-open
endif

Build Directory

构建目录

makefile
BUILDDIR := build
SOURCES := $(wildcard src/*.c)
OBJECTS := $(patsubst src/%.c,$(BUILDDIR)/%.o,$(SOURCES))

$(BUILDDIR)/%.o: src/%.c | $(BUILDDIR)
	$(CC) -c $< -o $@

$(BUILDDIR):
	mkdir -p $@
makefile
BUILDDIR := build
SOURCES := $(wildcard src/*.c)
OBJECTS := $(patsubst src/%.c,$(BUILDDIR)/%.o,$(SOURCES))

$(BUILDDIR)/%.o: src/%.c | $(BUILDDIR)
	$(CC) -c $< -o $@

$(BUILDDIR):
	mkdir -p $@

Environment Export

环境变量导出

makefile
export PYTHONPATH := $(PWD)/src
export DATABASE_URL

test:
	pytest tests/  # sees exported variables

makefile
export PYTHONPATH := $(PWD)/src
export DATABASE_URL

test:
	pytest tests/  # 子进程可访问导出的变量

Common Targets

常见目标

Quality Checks

质量检查

makefile
.PHONY: lint format check test

lint: ## Run linters
	ruff check .
	mypy src/

format: ## Format code
	ruff format .

check: format lint test ## All quality checks
makefile
.PHONY: lint format check test

lint: ## 运行代码检查
	ruff check .
	mypy src/

format: ## 格式化代码
	ruff format .

check: format lint test ## 执行所有质量检查

Cleanup

清理操作

makefile
.PHONY: clean clean-all

clean: ## Remove build artifacts
	rm -rf build/ dist/ *.egg-info
	find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true

clean-all: clean ## Remove all generated files
	rm -rf .venv .pytest_cache .mypy_cache
makefile
.PHONY: clean clean-all

clean: ## 移除构建产物
	rm -rf build/ dist/ *.egg-info
	find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true

clean-all: clean ## 移除所有生成文件
	rm -rf .venv .pytest_cache .mypy_cache

Docker Integration

Docker集成

makefile
IMAGE := myapp
VERSION := $(shell git describe --tags --always)

docker-build: ## Build Docker image
	docker build -t $(IMAGE):$(VERSION) .

docker-run: ## Run container
	docker run -d -p 8000:8000 $(IMAGE):$(VERSION)

makefile
IMAGE := myapp
VERSION := $(shell git describe --tags --always)

docker-build: ## 构建Docker镜像
	docker build -t $(IMAGE):$(VERSION) .

docker-run: ## 运行容器
	docker run -d -p 8000:8000 $(IMAGE):$(VERSION)

Recipe Execution

脚本执行

Each Line = Separate Shell

每行对应独立Shell

makefile
undefined
makefile
undefined

Won't work - cd lost between lines

无效 - cd的效果在换行后丢失

bad: cd subdir pwd # Still in original dir!
bad: cd subdir pwd # 仍在原目录!

Correct - combine commands

正确 - 合并命令

good: cd subdir && pwd
good: cd subdir && pwd

Or use line continuation

或使用行继续符

also-good: cd subdir &&
pwd &&
make
undefined
also-good: cd subdir &&
pwd &&
make
undefined

Silent and Error Handling

静默执行与错误处理

makefile
target:
	@echo "@ suppresses command echo"
	-rm -f maybe.txt    # - ignores errors
makefile
target:
	@echo "@ 用于抑制命令回显"
	-rm -f maybe.txt    # - 用于忽略错误

Parallel Execution

并行执行

bash
make -j4              # 4 parallel jobs
make -j4 lint test    # Run lint and test in parallel

bash
make -j4              # 4个并行任务
make -j4 lint test    # 并行运行lint和test

Output Discipline

输出规范

One line in, one line out. Avoid echo spam.
makefile
undefined
输入一行,输出一行。 避免过多冗余输出。
makefile
undefined

❌ Too chatty

❌ 过于冗长

start: @echo "Starting services..." docker compose up -d @echo "Waiting..." @sleep 3 @echo "Done!"
start: @echo "Starting services..." docker compose up -d @echo "Waiting..." @sleep 3 @echo "Done!"

✅ Concise

✅ 简洁明了

start: ## Start services @echo "Starting at http://localhost:8000 ..." @docker compose up -d @echo "Logs: docker compose logs -f"

---
start: ## 启动服务 @echo "服务启动地址:http://localhost:8000 ..." @docker compose up -d @echo "查看日志:docker compose logs -f"

---

Conditionals

条件判断

makefile
DEBUG ?= 0

ifeq ($(DEBUG),1)
    CFLAGS += -g -O0
else
    CFLAGS += -O2
endif

ifdef CI
    TEST_FLAGS := --ci
endif

makefile
DEBUG ?= 0

ifeq ($(DEBUG),1)
    CFLAGS += -g -O0
else
    CFLAGS += -O2
endif

ifdef CI
    TEST_FLAGS := --ci
endif

Including Files

文件包含

makefile
undefined
makefile
undefined

Required include (error if missing)

必需包含(文件缺失时报错)

include config.mk
include config.mk

Optional include (silent if missing)

可选包含(文件缺失时无提示)

-include local.mk -include .env

---
-include local.mk -include .env

---

Common Pitfalls

常见陷阱

PitfallProblemSolution
Spaces in recipesRecipes need TABUse actual TAB character
Missing .PHONY
make test
fails if
test
file exists
Declare
.PHONY: test
cd in recipesEach line is new shellUse
cd dir && command
=
vs
:=
confusion
Unexpected late expansionUse
:=
by default
Unexported varsSubprocesses don't see vars
export VAR
Complex shell in makeHard to maintainMove to external script

陷阱问题解决方案
脚本中使用空格脚本行需要使用TAB字符使用真实的TAB字符
缺少.PHONY若存在
test
文件,
make test
会执行失败
声明
.PHONY: test
脚本中的cd命令每行对应新的Shell使用
cd dir && command
=
:=
混淆
意外的延迟展开默认使用
:=
未导出变量子进程无法访问变量
export VAR
Make中嵌入复杂Shell脚本难以维护移至外部脚本

Quick Reference

快速参考

makefile
undefined
makefile
undefined

Makefile Template

Makefile 模板

.DEFAULT_GOAL := help SHELL := /bin/bash .SHELLFLAGS := -ec
.PHONY: help install test lint format clean
help: ## Show this help @grep -E '^[a-zA-Z_-]+:.?## .$$' $(MAKEFILE_LIST) |
awk 'BEGIN {FS = ":.*?## "}; {printf " %-15s %s\n", $$1, $$2}'
install: ## Install dependencies uv sync --extra dev
test: ## Run tests uv run pytest tests/ -v
lint: ## Run linters uv run ruff check .
format: ## Format code uv run ruff format .
clean: ## Clean artifacts rm -rf build/ dist/ .pytest_cache

---
.DEFAULT_GOAL := help SHELL := /bin/bash .SHELLFLAGS := -ec
.PHONY: help install test lint format clean
help: ## 显示本帮助信息 @grep -E '^[a-zA-Z_-]+:.?## .$$' $(MAKEFILE_LIST) |
awk 'BEGIN {FS = ":.*?## "}; {printf " %-15s %s\n", $$1, $$2}'
install: ## 安装依赖 uv sync --extra dev
test: ## 运行测试 uv run pytest tests/ -v
lint: ## 运行代码检查 uv run ruff check .
format: ## 格式化代码 uv run ruff format .
clean: ## 清理构建产物 rm -rf build/ dist/ .pytest_cache

---

See Also

扩展阅读