Makefile条件判断
📝 核心概念
使用条件判断,可以让make根据运行时的不同情况选择不同的执行分支。条件表达式可以是比较变量的值,或是比较变量和常量的值。
应用场景
条件判断在以下场景中特别有用:
- ✅ 编译器选择:根据不同编译器使用不同参数
- ✅ 平台适配:根据操作系统选择不同的库
- ✅ 调试模式:根据调试标志选择不同的编译选项
- ✅ 功能开关:根据特性标志启用或禁用功能
基础示例
编译器判断示例
下面的例子判断 $(CC) 变量是否为 gcc,如果是的话,则使用GNU函数编译目标。
libs_for_gcc = -lgnu
normal_libs =
foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
执行分析
在上面示例的这个规则中,目标 foo 可以根据变量 $(CC) 值来选取不同的函数库来编译程序。
🎯 关键字说明
| 关键字 | 作用 | 说明 |
|---|---|---|
| ifeq | 条件开始 | 指定一个条件表达式 |
| else | 条件分支 | 表达式为假的情况 |
| endif | 条件结束 | 任何条件表达式都应该以此结束 |
简洁写法
我们还可以把上面的例子写得更简洁一些:
libs_for_gcc = -lgnu
normal_libs =
ifeq ($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
foo: $(objects)
$(CC) -o foo $(objects) $(libs)
💡 优势
这种写法将条件判断与具体规则分离,使代码更加清晰和可维护。
语法详解
基本语法结构
简单条件语句
<conditional-directive>
<text-if-true>
endif
完整条件语句
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
条件关键字
其中 <conditional-directive> 表示条件关键字,共有四个:
| 关键字 | 功能 | 用途 |
|---|---|---|
| ifeq | 相等判断 | 比较两个值是否相同 |
| ifneq | 不等判断 | 比较两个值是否不同 |
| ifdef | 定义判断 | 测试变量是否有值 |
| ifndef | 未定义判断 | 测试变量是否未定义 |
条件关键字详解
1️⃣ ifeq - 相等判断
📝 语法格式
ifeq (<arg1>, <arg2>)
ifeq '<arg1>' '<arg2>'
ifeq "<arg1>" "<arg2>"
ifeq "<arg1>" '<arg2>'
ifeq '<arg1>' "<arg2>"
ifneq只是关键字不同,具体语法格式不再赘述
🎯 功能说明
比较参数 arg1 和 arg2 的值是否相同。参数中可以使用make的函数。
🌰 实际示例
基础比较
ifeq ($(CC),gcc)
CFLAGS += -Wall -Wextra
endif
结合函数使用
ifeq ($(strip $(foo)),)
<text-if-empty>
endif
📝 函数说明
这个示例中使用了strip函数,如果这个函数的返回值是空(Empty),那么<text-if-empty>就生效。
2️⃣ ifneq - 不等判断
🎯 功能说明
比较参数 arg1 和 arg2 的值是否不同,如果不同,则为真。和 ifeq 相反。
🌰 实际示例
ifneq ($(DEBUG),)
CFLAGS += -g -DDEBUG
else
CFLAGS += -O2 -DNDEBUG
endif
3️⃣ ifdef - 定义判断
📝 语法格式
ifdef <variable-name>
🎯 功能说明
如果变量 <variable-name> 的值非空,那么表达式为真。否则,表达式为假。
⚠️ 重要特性
| 特性 | 说明 |
|---|---|
| 只测试值 | 只测试变量是否有值 |
| 不扩展变量 | 不会把变量扩展到当前位置 |
| 支持函数 | 变量名可以是函数的返回值 |
🌰 对比示例
示例一:间接定义
bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif
结果: $(frobozz) 值是 yes
示例二:直接为空
foo =
ifdef foo
frobozz = yes
else
frobozz = no
endif
结果: $(frobozz) 值是 no
📊 结果分析
| 示例 | foo的定义 | ifdef foo结果 | frobozz值 | 原因 |
|---|---|---|---|---|
| 示例一 | foo = $(bar) |
真 | yes |
foo有定义(虽然bar为空) |
| 示例二 | foo = |
假 | no |
foo定义为空 |
4️⃣ ifndef - 未定义判断
📝 语法格式
ifndef <variable-name>
🎯 功能说明
和 ifdef 是相反的意思。如果变量未定义或为空,则为真。
🌰 实际示例
ifndef CC
CC = gcc
endif
ifndef CFLAGS
CFLAGS = -Wall -O2
endif
💡 常用场景
常用于设置默认值,只有在变量未定义时才设置。
语法规则和注意事项
格式要求
✅ 允许的格式
| 要求 | 说明 | 示例 |
|---|---|---|
| 空格允许 | 条件指令行上可以有多余空格 | ifeq ($(CC),gcc) |
| 注释安全 | 可以使用注释符 # |
ifeq ($(CC),gcc) # 检查编译器 |
| else/endif | 同样不能以Tab开始 | else 和 endif |
❌ 禁止的格式
| 禁止 | 原因 | 正确做法 |
|---|---|---|
| Tab开始 | 会被认为是命令 | 使用空格开始 |
计算时机
⚠️ 重要提醒
make是在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句。
🚫 避免使用自动化变量
# ❌ 错误示例 - 不要这样做
ifeq ($@,target)
# 自动化变量在读取时还没有值
endif
# ✅ 正确示例
ifeq ($(TARGET),debug)
CFLAGS += -g
endif
📝 原因说明
最好不要把自动化变量(如$@等)放入条件表达式中,因为自动化变量是在运行时才有的。
高级应用示例
多条件判断
# 根据操作系统选择不同设置
ifeq ($(OS),Windows_NT)
EXECUTABLE_SUFFIX = .exe
PATH_SEPARATOR = \\
else ifeq ($(shell uname),Darwin)
EXECUTABLE_SUFFIX =
PATH_SEPARATOR = /
CFLAGS += -mmacosx-version-min=10.9
else
EXECUTABLE_SUFFIX =
PATH_SEPARATOR = /
endif
嵌套条件
ifdef DEBUG
CFLAGS += -g
ifeq ($(DEBUG),verbose)
CFLAGS += -DDEBUG_VERBOSE
endif
else
CFLAGS += -O2 -DNDEBUG
ifneq ($(OPTIMIZE),no)
CFLAGS += -O3
endif
endif
功能开关
# 功能开关示例
ifdef ENABLE_SSL
CFLAGS += -DUSE_SSL
LIBS += -lssl -lcrypto
endif
ifdef ENABLE_THREADS
CFLAGS += -pthread -DUSE_THREADS
LIBS += -lpthread
endif
ifndef DISABLE_WARNINGS
CFLAGS += -Wall -Wextra -Werror
endif
版本检查
# 检查编译器版本
GCC_VERSION := $(shell gcc -dumpversion)
ifeq ($(shell expr $(GCC_VERSION) \>= 4.9), 1)
CFLAGS += -fstack-protector-strong
else
CFLAGS += -fstack-protector
endif
最佳实践
实践建议
| 建议 | 说明 | 示例 |
|---|---|---|
| 使用有意义的变量名 | 提高可读性 | DEBUG 而不是 D |
| 添加注释 | 解释条件判断的目的 | # 检查是否启用调试模式 |
| 保持简洁 | 避免过度嵌套 | 最多2-3层嵌套 |
| 设置默认值 | 使用 ifndef 设置默认值 |
ifndef CC |
常用模式
默认值设置
ifndef CC
CC = gcc
endif
ifndef CFLAGS
CFLAGS = -Wall -O2
endif
调试/发布模式
ifeq ($(BUILD_TYPE),debug)
CFLAGS += -g -DDEBUG
LDFLAGS += -g
else
CFLAGS += -O2 -DNDEBUG
LDFLAGS += -s
endif
平台检测
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
PLATFORM_LIBS = -lrt
endif
ifeq ($(UNAME_S),Darwin)
PLATFORM_LIBS = -framework CoreFoundation
endif
📋 总结
- ✅ Makefile条件判断的基本概念和应用场景
- ✅ 四种条件关键字的语法和用法
- ✅ 条件表达式的计算时机和注意事项
- ✅ 高级应用示例和最佳实践
- ✅ 常见错误和避免方法

浙公网安备 33010602011771号