Android构建系统:Android.mk(3)条件控制详解

在上一篇博客中,我介绍了Android.mk文件赋值操作、变量引用和函数的使用方法,静态库和可执行文件的示例。在本文中,将介绍Android.mk文件中条件控制的使用方法和一些常用的控制语句。通过使用条件控制,可以根据不同的情况执行不同的操作,可以实现一些复杂的逻辑。我会附上详细的测试代码和测试结果。

Android构建系统:Android.mk(1)基础概念赋值变量引用详解
Android构建系统:Android.mk(2)函数详解
Android构建系统:Android.mk(3)条件控制详解

测试代码和结果

完整的测试结果:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
完整的测试代码:

$(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变量:

  1. BUILD_STATIC_LIBRARY: 用于构建静态库(.a文件)。
  2. BUILD_SHARED_LIBRARY: 用于构建动态库(.so文件)。
  3. BUILD_HOST_EXECUTABLE: 用于构建在构建机上运行的可执行文件,而不是在目标设备上。
  4. BUILD_HOST_STATIC_LIBRARY: 用于构建在构建机上使用的静态库。
  5. BUILD_HOST_SHARED_LIBRARY: 用于构建在构建机上使用的动态库。
  6. BUILD_JAVA_LIBRARY: 用于构建Java库。
  7. 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 是否未定义
elseifeq, 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-includesinclude 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文件中条件控制的使用方法和一些常用的控制语句。通过使用条件控制,可以根据不同的情况执行不同的操作,可以实现复杂的逻辑。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值