在上一篇博客中,我介绍了Android.mk文件赋值操作、变量引用和函数的使用方法,静态库和可执行文件的示例。在本文中,将介绍Android.mk文件中条件控制的使用方法和一些常用的控制语句。通过使用条件控制,可以根据不同的情况执行不同的操作,可以实现一些复杂的逻辑。我会附上详细的测试代码和测试结果。
测试代码和结果
完整的测试结果:
完整的测试代码:
$(warning ====== 开始测试 ======) # 测试ifeq # 比较两个相同的字符串 ifeq (apple,apple) $(info [IFEQ] apple 和 apple 是相同的) endif # 比较两个不同的字符串 ifeq "banana" "orange" $(info [IFEQ] banana 和 orange 是相同的) else $(info [IFEQ] banana 和 orange 是不同的) endif # 使用ifeq 结构检查环境变量 ifeq ($(OS),Linux) $(info [IFEQ] 操作系统是 Linux) endif ifeq "Linux" "Android" # 如果Linux 和Android 相等,则执行这里的代码块(不会执行) $(info Linux 和 Android 是相同的) endif # 测试ifneq # 比较两个不同的字符串 ifneq (grape,lemon) $(info [IFNEQ] grape 和 lemon 是不同的) endif # 使用ifneq 结构检查环境变量 ifneq ($(OS),Windows) $(info [IFNEQ] 操作系统不是 Windows) endif # 测试ifdef # 定义一个变量 FRUIT := apple ifdef FRUIT $(info [IFDEF] FRUIT 已定义,其值是 $(FRUIT)) endif # 测试ifndef # 检查一个未定义的变量 ifndef VEGETABLE $(info [IFNDEF] VEGETABLE 未定义) endif # 使用else 结构 # 检查环境变量 ifeq ($(OS),Linux) $(info [ELSE] 操作系统是 Linux) else $(info [ELSE] 操作系统不是 Linux) endif # 使用嵌套的ifeq 和 else 来模拟elif # 检查多个可能的操作系统值 ifeq ($(OS),Linux) $(info [ELIF] 操作系统是 Linux) else ifeq ($(OS),Windows) $(info [ELIF] 操作系统是 Windows) else ifeq ($(OS),Mac) $(info [ELIF] 操作系统是 Mac) else $(info [ELIF] 操作系统是未知的) endif endif endif # 使用include 包含另一个makefile include common.mk $(info [INCLUDE] 已包含 common.mk) # include common_a.mk # Android.mk:80: common_a.mk: 文件或目录不存在 # make: *** 没有规则可制作目标“common_a.mk”。 停止。 -include common_b.mk # 没有common_b.mk # 使用sinclude 安静地包含另一个makefile sinclude optional.mk $(info [SINCLUDE] 尝试包含 optional.mk) # 展示 override 和 :=,= 的区别 MY_VAR := original_value MY_LAZY_VAR = $(MY_VAR) override MY_VAR = new_value $(info [OVERRIDE] MY_VAR 原来设置为 original_value,但现在是 $(MY_VAR)) $(info [LAZY EVALUATION] MY_LAZY_VAR 的值是 $(MY_LAZY_VAR)) # 使用export 导出变量到子进程 export MY_VAR $(shell echo [SHELL EXPORT] shell中的 MY_VAR 的值是 $(MY_VAR) > shell_output.txt) $(info [EXPORT] 检查 shell_output.txt 查看在 shell 中 MY_VAR 的值) # 使用unexport 阻止之前导出的变量在子进程中被访问 unexport MY_VAR $(shell echo [SHELL UNEXPORT] shell中的 MY_VAR 的值是 $(MY_VAR) > shell_unexport_output.txt) $(info [UNEXPORT] 检查 shell_unexport_output.txt 查看在 shell 中 MY_VAR 的值在取消导出后) # 使用vpath 设置搜索路径 vpath %.cpp src FOUND_FILES := $(wildcard src/*.cpp) $(info [VPATH] 找到的 .cpp 文件: $(FOUND_FILES)) TARGET_ARCH := arm LOCAL_MODULE_TAGS :=lrt # 使用ifeq 结构 ifeq ($(TARGET_ARCH),arm) # 如果目标架构为arm,则执行这里的代码块 LOCAL_CFLAGS += -DARM LOCAL_SRC_FILES := src/arm_specific.cpp endif ifeq ($(HOST_OS),linux) # 如果主机操作系统为linux,则执行这里的代码块 LOCAL_LDLIBS += -lrt endif # 使用ifdef 结构 ifdef LOCAL_MODULE_TAGS # 如果LOCAL_MODULE_TAGS 已经定义,则执行这里的代码块 LOCAL_CFLAGS += -D$(LOCAL_MODULE_TAGS) endif ifdef LOCAL_JNI_SHARED_LIBRARIES # 如果LOCAL_JNI_SHARED_LIBRARIES 已经定义,则执行这里的代码块 include $(CLEAR_VARS) LOCAL_MODULE := $(LOCAL_JNI_SHARED_LIBRARIES)-copy LOCAL_SRC_FILES := $(TARGET_OUT_INTERMEDIATE_LIBRARIES)/$(LOCAL_JNI_SHARED_LIBRARIES).so include $(BUILD_PREBUILT) endif # 使用else 结构 ifeq ($(TARGET_ARCH),arm) # 如果目标架构为arm,则执行这里的代码块 LOCAL_CFLAGS += -DARM else # 添加一个分支 # 如果目标架构不为arm,则执行这里的代码块 LOCAL_CFLAGS += -DOTHER_ARCH endif ifdef LOCAL_MODULE_TAGS # 如果LOCAL_MODULE_TAGS 已经定义,则执行这里的代码块 LOCAL_CFLAGS += -D$(LOCAL_MODULE_TAGS) else # 添加一个分支 # 如果LOCAL_MODULE_TAGS 未定义,则执行这里的代码块 $(error LOCAL_MODULE_TAGS is not defined) endif # 使用elif 结构 ifeq ($(TARGET_ARCH),arm) # 如果目标架构为arm,则执行这里的代码块 LOCAL_CFLAGS += -DARM LOCAL_SRC_FILES := src/arm_specific.cpp else ifeq ($(TARGET_ARCH),x86) # 如果目标架构为x86,则执行这里的代码块 LOCAL_CFLAGS += -DX86 LOCAL_SRC_FILES := src/x86_specific.cpp else ifeq ($(TARGET_ARCH),mips) # 如果目标架构为mips,则执行这里的代码块 LOCAL_CFLAGS += -DMIPS LOCAL_SRC_FILES := src/mips_specific.cpp else # 可选,添加一个分支 # 如果目标架构都不是arm、x86、mips,则执行这里的代码块 $(error Unknown target arch: $(TARGET_ARCH)) endif $(warning ====== 测试结束 ======) all: @echo "测试example03结束!"
1. 条件语句
条件语句用于根据条件执行不同的操作,它有以下几种结构:
- ifeq:这个结构用于判断两个字符串是否相等,如果相等则执行后面的代码块,否则跳过。它的一般形式为:
# 测试ifeq # 比较两个相同的字符串 ifeq (apple,apple) $(info [IFEQ] apple 和 apple 是相同的) endif
或者
# 比较两个不同的字符串 ifeq "banana" "orange" $(info [IFEQ] banana 和 orange 是相同的) else $(info [IFEQ] banana 和 orange 是不同的) endif
例如:
# 使用ifeq 结构检查环境变量 ifeq ($(OS),Linux) $(info [IFEQ] 操作系统是 Linux) endif ifeq "Linux" "Android" # 如果Linux 和Android 相等,则执行这里的代码块(不会执行) $(info Linux 和 Android 是相同的) endif
- ifneq:这个结构用于判断两个字符串是否不相等,如果不相等则执行后面的代码块,否则跳过。它的一般形式与ifeq 相同,只是将
ifeq替换为ifneq。例如:
# 测试ifneq # 比较两个不同的字符串 ifneq (grape,lemon) $(info [IFNEQ] grape 和 lemon 是不同的) endif # 使用ifneq 结构检查环境变量 ifneq ($(OS),Windows) $(info [IFNEQ] 操作系统不是 Windows) endif
- ifdef:这个结构用于判断一个变量是否已经定义,如果已经定义则执行后面的代码块,否则跳过。它的一般形式为:
ifdef 变量名 # 如果变量名已经定义,则执行这里的代码块 endif
例如:
# 测试ifdef # 定义一个变量 FRUIT := apple ifdef FRUIT $(info [IFDEF] FRUIT 已定义,其值是 $(FRUIT)) endif
- ifndef:这个结构用于判断一个变量是否未定义,如果未定义,则执行后面的代码块,否则跳过。它的一般形式与ifdef 相同,只是将ifdef 替换为ifndef。例如:
# 测试ifndef # 检查一个未定义的变量 ifndef VEGETABLE $(info [IFNDEF] VEGETABLE 未定义) endif
- else:这个结构用于在条件语句中添加一个分支,如果前面的条件不成立,则执行后面的代码块。例如:
# 使用else 结构 # 检查环境变量 ifeq ($(OS),Linux) $(info [ELSE] 操作系统是 Linux) else $(info [ELSE] 操作系统不是 Linux) endif
- elif:这个结构用于在条件语句中添加一个或多个分支,如果前面的条件不成立,但后面的条件成立,则执行后面的代码块。它的一般形式为:
if... # 条件语句的开始 # 如果条件成立,则执行这里的代码块 elif... # 添加一个或多个分支 # 如果条件成立,则执行这里的代码块 else # 可选,添加一个分支 # 如果条件不成立,则执行这里的代码块 endif # 条件语句的结束
例如:
# 使用嵌套的ifeq 和 else 来模拟elif # 检查多个可能的操作系统值 ifeq ($(OS),Linux) $(info [ELIF] 操作系统是 Linux) else ifeq ($(OS),Windows) $(info [ELIF] 操作系统是 Windows) else ifeq ($(OS),Mac) $(info [ELIF] 操作系统是 Mac) else $(info [ELIF] 操作系统是未知的) endif endif endif
2. 控制语句
控制语句用于控制代码块的执行流程,有以下几种控制关键字可供选择:
- include:这个关键字用于包含其他的Makefile 文件,它接受一个或多个文件名作为参数,它会将这些文件中的内容插入到当前位置。例如:
# 使用include 关键字 include $(CLEAR_VARS) # 包含CLEAR_VARS 文件,用于清空一些变量 include $(BUILD_EXECUTABLE) # 包含BUILD_EXECUTABLE 文件,用于构建可执行模块 include $(BUILD_PACKAGE) # 包含BUILD_PACKAGE 文件,用于构建APK模块 # 使用include 包含另一个makefile include common.mk $(info [INCLUDE] 已包含 common.mk)
以下是一些常见的include
变量:
BUILD_STATIC_LIBRARY
: 用于构建静态库(.a
文件)。BUILD_SHARED_LIBRARY
: 用于构建动态库(.so
文件)。BUILD_HOST_EXECUTABLE
: 用于构建在构建机上运行的可执行文件,而不是在目标设备上。BUILD_HOST_STATIC_LIBRARY
: 用于构建在构建机上使用的静态库。BUILD_HOST_SHARED_LIBRARY
: 用于构建在构建机上使用的动态库。BUILD_JAVA_LIBRARY
: 用于构建Java库。BUILD_PREBUILT
: 用于包含预先构建的二进制或其他文件。
- -include:这个关键字与include 相同,只是在包含文件时不会报错,即使文件不存在或无法读取。例如:
# 使用-include 关键字 -include common_b.mk # 包含common_b.mk 文件,如果不存在或无法读取,不会报错
- sinclude:这个关键字与-include 相同,只是使用了不同的拼写。例如:
# 使用sinclude 安静地包含另一个makefile sinclude optional.mk $(info [SINCLUDE] Tried to include optional.mk) # 包含optional.mk 文件,如果不存在或无法读取,不会报错
- override:这个关键字用于覆盖命令行中指定的变量值,它接受一个赋值操作作为参数,它会将赋值操作中的变量值覆盖命令行中的变量值。例如:
# 展示 override 和 :=,= 的区别 MY_VAR := original_value MY_LAZY_VAR = $(MY_VAR) override MY_VAR = new_value $(info [OVERRIDE] MY_VAR 原来设置为 original_value,但现在是 $(MY_VAR)) $(info [LAZY EVALUATION] MY_LAZY_VAR 的值是 $(MY_LAZY_VAR))
- export:这个关键字用于导出变量到子进程中,它接受一个或多个变量名作为参数,它会将这些变量作为环境变量传递给子进程。例如:
# 使用export 关键字 export NDK_PROJECT_PATH := $(LOCAL_PATH) # 导出NDK_PROJECT_PATH 到子进程中,用于指定项目路径 export APP_ABI := armeabi-v7a # 导出APP_ABI 到子进程中,用于指定目标架构 # 使用export 导出变量到子进程 export MY_VAR $(shell echo [SHELL EXPORT] shell中的 MY_VAR 的值是 $(MY_VAR) > shell_output.txt) $(info [EXPORT] 检查 shell_output.txt 查看在 shell 中 MY_VAR 的值)
- unexport:这个关键字用于取消导出变量到子进程中,它接受一个或多个变量名作为参数,它会将这些变量从环境变量中移除。例如:
# 使用unexport 阻止之前导出的变量在子进程中被访问 unexport MY_VAR $(shell echo [SHELL UNEXPORT] shell中的 MY_VAR 的值是 $(MY_VAR) > shell_unexport_output.txt) $(info [UNEXPORT] 检查 shell_unexport_output.txt 查看在 shell 中 MY_VAR 的值在取消导出后) $(shell echo $$MY_VAR) # 在子进程中打印MY_VAR的值(为空)
- vpath:这个关键字用于指定搜索路径,它接受一个或两个参数,第一个参数是匹配模式,第二个参数是搜索路径(可选),它会在搜索路径中查找匹配模式的文件。例如:
# 使用vpath 关键字 vpath %.c src # 指定在src 目录下查找以.c 结尾的文件 vpath %.java java # 指定在java 目录下查找以.java 结尾的文件 vpath AndroidManifest.xml manifest # 指定在manifest 目录下查找AndroidManifest.xml 文件 # 使用vpath 设置搜索路径 vpath %.cpp src FOUND_FILES := $(wildcard src/*.cpp) $(info [VPATH] 找到的 .cpp 文件: $(FOUND_FILES))
3. 条件控制的应用
条件控制在Android.mk 文件中有很多实际应用,例如:
- ifeq:可以使用ifeq 结构来根据不同的平台或架构执行不同的操作,例如:
TARGET_ARCH := arm LOCAL_MODULE_TAGS :=lrt # 使用ifeq 结构 ifeq ($(TARGET_ARCH),arm) # 如果目标架构为arm,则执行这里的代码块 LOCAL_CFLAGS += -DARM LOCAL_SRC_FILES := src/arm_specific.cpp endif ifeq ($(HOST_OS),linux) # 如果主机操作系统为linux,则执行这里的代码块 LOCAL_LDLIBS += -lrt endif
- ifdef:可以使用ifdef 结构来判断一些特定的变量是否已经定义,例如:
# 使用ifdef 结构 ifdef LOCAL_MODULE_TAGS # 如果LOCAL_MODULE_TAGS 已经定义,则执行这里的代码块 LOCAL_CFLAGS += -D$(LOCAL_MODULE_TAGS) endif ifdef LOCAL_JNI_SHARED_LIBRARIES # 如果LOCAL_JNI_SHARED_LIBRARIES 已经定义,则执行这里的代码块 include $(CLEAR_VARS) LOCAL_MODULE := $(LOCAL_JNI_SHARED_LIBRARIES)-copy LOCAL_SRC_FILES := $(TARGET_OUT_INTERMEDIATE_LIBRARIES)/$(LOCAL_JNI_SHARED_LIBRARIES).so include $(BUILD_PREBUILT) endif
- else:可以使用else 结构来添加一个分支,以处理条件不成立的情况,例如:
# 使用else 结构 ifeq ($(TARGET_ARCH),arm) # 如果目标架构为arm,则执行这里的代码块 LOCAL_CFLAGS += -DARM else # 添加一个分支 # 如果目标架构不为arm,则执行这里的代码块 LOCAL_CFLAGS += -DOTHER_ARCH endif ifdef LOCAL_MODULE_TAGS # 如果LOCAL_MODULE_TAGS 已经定义,则执行这里的代码块 LOCAL_CFLAGS += -D$(LOCAL_MODULE_TAGS) else # 添加一个分支 # 如果LOCAL_MODULE_TAGS 未定义,则执行这里的代码块 $(error LOCAL_MODULE_TAGS is not defined) endif
- elif:可以使用elif 结构来添加一个或多个分支,以处理多种条件情况,例如:
# 使用elif 结构 ifeq ($(TARGET_ARCH),arm) # 如果目标架构为arm,则执行这里的代码块 LOCAL_CFLAGS += -DARM LOCAL_SRC_FILES := src/arm_specific.cpp else ifeq ($(TARGET_ARCH),x86) # 如果目标架构为x86,则执行这里的代码块 LOCAL_CFLAGS += -DX86 LOCAL_SRC_FILES := src/x86_specific.cpp else ifeq ($(TARGET_ARCH),mips) # 如果目标架构为mips,则执行这里的代码块 LOCAL_CFLAGS += -DMIPS LOCAL_SRC_FILES := src/mips_specific.cpp else # 可选,添加一个分支 # 如果目标架构都不是arm、x86、mips,则执行这里的代码块 $(error Unknown target arch: $(TARGET_ARCH)) endif
根据本文的代码例子,涉及的关键字进行总结成详细的表格:
关键字/结构 | 描述 | 示例 |
ifeq |
比较两个字符串是否相等 | ifeq ($(TARGET_ARCH),arm) 检查 TARGET_ARCH 是否为 arm |
ifneq |
比较两个字符串是否不相等 | ifneq ($(OS),Windows) 检查 OS 是否不为 Windows |
ifdef |
检查一个变量是否已定义 | ifdef LOCAL_MODULE_TAGS 检查 LOCAL_MODULE_TAGS 是否已定义 |
ifndef |
检查一个变量是否未定义 | ifndef VEGETABLE 检查 VEGETABLE 是否未定义 |
else |
为 ifeq , ifneq , ifdef , ifndef 提供一个分支 |
ifeq ($(OS),Linux) ... else ... endif |
include |
包含另一个 makefile 文件 | include common.mk 包含 common.mk 文件 |
-include |
安静地包含另一个 makefile 文件,即使它不存在也不会报错 | -include common_b.mk 尝试包含 common_b.mk 文件 |
sinclude |
同 -include |
sinclude optional.mk 尝试包含 optional.mk 文件 |
override |
覆盖之前定义的变量 | override MY_VAR = new_value 覆盖 MY_VAR 的值 |
export |
导出变量到子进程 | export MY_VAR 导出 MY_VAR 以供子进程使用 |
unexport |
阻止之前导出的变量在子进程中被访问 | unexport MY_VAR 取消导出 MY_VAR |
vpath |
设置目录搜索路径 | vpath %.cpp src 设置 .cpp 文件的搜索路径为 src 目录 |
$(shell ...) |
执行 shell 命令 | $(shell echo [SHELL EXPORT] MY_VAR in shell is $(MY_VAR) > shell_output.txt) 执行 shell 命令并将输出重定向到 shell_output.txt |
$(wildcard ...) |
查找匹配模式的文件 | FOUND_FILES := $(wildcard src/*.cpp) 查找 src 目录下的所有 .cpp 文件 |
$(error ...) |
抛出一个错误 | $(error LOCAL_MODULE_TAGS is not defined) 如果 LOCAL_MODULE_TAGS 未定义,则抛出错误 |
4. 总结
本文介绍了Android.mk文件中条件控制的使用方法和一些常用的控制语句。通过使用条件控制,可以根据不同的情况执行不同的操作,可以实现复杂的逻辑。