Makefile 是组织编译的命令脚本文件,通过描述目标与依赖之间的关系树,自动化构建生成可执行文件。
make根据 Makefile 中定义的规则关系树,按依赖顺序构建出可执行程序。
# 目标: 依赖
# 命令
a.out: demo.c
gcc -o $@ $<
make 指令
Windows 环境
运行 make -v 可能出现:
'make' 不是内部命令或外部命令, 也不是可执行的程序, 或批处理文件.
# 系统在环境变量中找不到 make 命令的执行文件
解决方法:
- 找到
mingw安装目录,进入bin目录,找到mingw32-make.exe - 将
mingw32-make.exe重命名为make.exe
编译过程

Makefile 核心语法
变量
使用 $(val) 获取变量 val 的值。
.PHONY: clean
CC=g++
BIN=exec
OBJS=AddTwoSum.o utils.o
$(BIN): $(OBJS)
@echo "start compiling..."
@echo $(CC)
$(CC) -o $(BIN) $(OBJS)
@echo "compile done"
AddTwoSum.o: AddTwoSum.cpp
$(CC) -c -o $@ $<
utils.o: utils.cpp
$(CC) -c -o $@ $<
clean:
rm -f $(BIN) $(OBJS)
条件赋值
# 条件赋值 ?= —— 仅在变量未定义时赋值
# 追加赋值 += —— 向变量追加值
CC = g++
CC ?= gcc # CC 已定义,此句不生效
OBJS = AddTwoSum.o
OBJS += utils.o # 追加
echo:
@echo $(CC)
@echo $(OBJS)
立即变量与延迟变量
# 使用 := 赋值 —— 立即变量(定义时马上展开)
# 使用 = 赋值 —— 延迟变量(使用时才展开,保留变量引用关系)
a = 1
b = 2
val_a := $(a) # val_a = 1(立即求值)
val_b = $(b) # val_b 的值随 b 变化(延迟求值)
a = 10
echo:
@echo $(val_a)
@echo $(val_b)
自动变量
# 常用自动变量
# $@ —— 目标
# $^ —— 所有依赖项
# $< —— 依赖项中的第一个
# $? —— 所有依赖项中被修改过的
.PHONY: clean
CC=g++
BIN=exec
OBJS=AddTwoSum.o utils.o
$(BIN): $(OBJS)
@echo "start compiling..."
@echo $(CC)
$(CC) -o $@ $^
@echo "compile done"
AddTwoSum.o: AddTwoSum.cpp
$(CC) -c -o $@ $<
utils.o: utils.cpp
$(CC) -c -o $@ $<
clean:
rm -f $(BIN) $(OBJS)
变量替换(模式匹配)
.PHONY: echo
SRC = demo.c demo1.c
OBJ = $(SRC:.c=.o)
OBJ1 = $(SRC:%.c=%.o)
echo:
@echo "SRC=$(SRC)"
@echo "OBJ=$(OBJ)"
@echo "OBJ1=$(OBJ1)"
# 输出:
# SRC=demo.c demo1.c
# OBJ=demo.o demo1.o
# OBJ1=demo.o demo1.o
环境变量
环境变量在运行时载入 Makefile。若存在同名变量,系统环境变量会被 Makefile 中定义的变量覆盖。
.PHONY: echo
CFLAGS = -g
echo:
@echo "CFLAGS = $(CFLAGS)"
@echo "SHELL = $(SHELL)"
@echo "MAKE = $(MAKE)"
@echo "HOSTNAME = $(HOSTNAME)"
# 输出示例:
# CFLAGS = -g
# SHELL = /usr/bin/sh
# MAKE = /usr/bin/make
# HOSTNAME = TX-PC0N4UHP
# 运行时可通过命令行传参覆盖:
# make HOSTNAME=CodeMax
# HOSTNAME = CodeMax
条件判断
# ifeq —— 判断相等
mode = debug
echo:
ifeq ($(mode),debug)
@echo "debug mode"
else
@echo "release mode"
endif
# ifneq —— 判断不等
echo:
ifeq ($(mode),)
@echo "mode is empty"
else
@echo "mode is not empty"
endif
# ifdef —— 判断变量是否已定义
echo:
ifdef mode
@echo "mode is defined"
else
@echo "mode is not defined"
endif
函数
函数分为三类:
- 自定义函数 —— 通过
call调用 - 内嵌函数 —— Make 内置,直接调用
- 函数名与参数之间用空格分隔,参数之间用逗号分隔
# 内嵌函数示例 —— wildcard
SRC = $(wildcard *.cpp)
echo:
@echo "SRC = $(SRC)"
# 自定义函数示例
define func
@echo "param0 = $(0)"
@echo "param1 = $(1)"
endef
echo:
$(call func, hello world)
foreach 遍历
$(foreach VAR, LIST, TEXT)
VAR—— 将 LIST 中空格分隔的每一项依次赋值给 VARTEXT—— 使用 VAR 进行处理的脚本
if 函数
# $(if condition, then[, else])
install__path =
install_path = $(if $(install__path), $(install__path), /usr/local)
echo:
@echo "$(install_path)"
call 函数
# $(call def_func, <param1>, <param2>...)
error / warning 函数
# $(error TEXT...) —— 抛出错误并终止
# $(warning TEXT...) —— 输出警告并继续
完整示例
# 伪目标声明:防止当前目录有名为 clean 的文件导致规则跳过
.PHONY: clean
# 编译器
CC = g++
# 编译参数
# -g 加入调试信息
# -DDEBUG 编译 debug 版本
# -W -Wall 输出所有警告
# -fPIC 生成动态库时使用
CFLAGS = -g -DDEBUG -W -Wall -fPIC
# 头文件目录
HEADER = -I./
ALGONAME = AddTwoSum
TOOLNAME = utils
OBJS= $(ALGONAME).o $(TOOLNAME).o
TARGET = algo
$(TARGET): $(OBJS)
@echo "start compiling..."
$(CC) $(CFLAGS) -o $@ $^
@echo "compile done"
$(ALGONAME).o: $(ALGONAME).cpp
$(CC) -c -o $@ $<
$(TOOLNAME).o: $(TOOLNAME).cpp
$(CC) -c -o $@ $<
clean:
rm -f $(TARGET) $(OBJS)