Android.mk必读的一些基础知识部分

背景:

经常在在对系统进行相关修改的时候,需要阅读或者修改Android.mk,以前我们学习或者编写mk文件,一般都是定义一个固定的目标模板,比如定义一个apk目标,定义一个native程序目标等。
比如下面apk编译模板:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

# 设置模块名为myapp
LOCAL_MODULE := myapp
# 添加需要编译的Java源文件
LOCAL_SRC_FILES := $(wildcard *.java)
# 添加需要编译的资源文件
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
# 设置输出APK的路径和名称
LOCAL_PACKAGE_NAME := myapp
LOCAL_PACKAGE_OUTPUT_FILE := $(LOCAL_PATH)/$(LOCAL_PACKAGE_NAME).apk

# 设置系统权限和目录
LOCAL_CERTIFICATE := platform
LOCAL_PRIVATE_PLATFORM_APIS := true
# 设置是否编译在 priv-app,如果没有指定目录默认为 system/priv-app
LOCAL_PRIVILEGED_MODULE := true 

include $(BUILD_PACKAGE)

当时属于没有去深入mk相关的一些语法和函数,大部分需要用时候都是依葫芦画瓢方式,今天我们来对部分常用的mk基础知识做一个讲解,这样在后需要用到mk就知道其原理了。

mk基本符号说明

赋值部分:

= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值

+= 是添加等号后面的值
而 = 与 := 的区别在于,= 会在makefile 展开后再决定变量的值,即最后被指定的值

            x = foo
            y = $(x) bar
            x = xyz

      在上例中,y的值将会是 xyz bar ,而不是 foo bar 。

单个 $ —— 变量引用或函数调用‌

‌作用‌:用于引用变量或调用函数。
‌语法‌:
$(VAR_NAME) 或 ${VAR_NAME} —— 引用变量 VAR_NAME 的值。
$(call func,arg1,arg2) —— 调用自定义函数 func 并传入参数。

双 $$ —— 转义 $,使其在 Shell 命令中保留‌

‌作用‌:在 $(shell …) 或 ( f o r e a c h . . . ) 等需要执行 S h e l l 命令的上下文中, (foreach ...) 等需要执行 Shell 命令的上下文中, (foreach...)等需要执行Shell命令的上下文中,$ 会被转义成单个 $,避免被 Make 解析。
‌常见场景‌:
‌在 $(shell …) 中使用环境变量‌:

BUILD_TIME := $(shell date +%Y%m%d)  # 直接使用 Shell 命令
JAVA_HOME := $(shell echo $$JAVA_HOME)  # 使用 $$ 获取 Shell 环境变量

为什么 $$ 在 $(shell …) 里是必要的?‌

因为 $(shell …) 会执行 Shell 命令,而 $VAR 在 Shell 中表示变量。如果直接写 $PATH,Make 会尝试解析 PATH 变量,而不是传递给 Shell。

常见一些方法和关键字说明

call关键字
‌宏函数调用‌
$(call macro,param1,param2)会展开宏定义,并将参数$1、$2等替换为传入的值

define greet
  $(info Hello, $1!)
endef
$(call greet,World)  # 输出"Hello, World!"

比如最常见的的

LOCAL_PATH := $(call my-dir)

调用获取当前mk的路径,这里的my-dir实际是一个宏函数,定义在如下文件:
build/core/definitions.mk


###########################################################
## Retrieve the directory of the current makefile
## Must be called before including any other makefile!!
###########################################################

# Figure out where we are.
define my-dir
$(strip \
  $(eval LOCAL_MODULE_MAKEFILE := $$(lastword $$(MAKEFILE_LIST))) \
  $(if $(filter $(BUILD_SYSTEM)/% $(OUT_DIR)/%,$(LOCAL_MODULE_MAKEFILE)), \
    $(error my-dir must be called before including any other makefile.) \
   , \
    $(patsubst %/,%,$(dir $(LOCAL_MODULE_MAKEFILE))) \
   ) \
 )
endef

eval 关键字
eval 是 GNU Make 的关键字,主要用于‌动态生成 Makefile 代码‌,其核心作用与典型用法如下:
动态代码生成‌
eval 会将传入的字符串‌解析为 Makefile 代码并执行‌,常用于构建过程中动态生成编译规则或变量定义

define add_module  
  $(eval include $(CLEAR_VARS))  # 动态插入清除变量的操作  
  $(eval LOCAL_MODULE := $1)  
  $(eval include $(BUILD_SHARED_LIBRARY))  
endef  
$(call add_module,foo)  

常见其他函数说明

include部分

基础语法‌

include $(FILENAME)

用于包含指定的Makefile文件

忽略错误‌

前缀-表示若文件不存在则忽略错误继续编译:

-include $(PATH_TO_FILE)  # 文件缺失时不报错

常用预定义规则‌

$(CLEAR_VARS):清除模块变量(如LOCAL_MODULE),需在每个模块开头使用。
$(BUILD_XXX):指定编译类型,如:

include $(BUILD_SHARED_LIBRARY)  # 编译动态库
include $(BUILD_STATIC_LIBRARY)  # 编译静态库
include $(BUILD_EXECUTABLE)     # 编译可执行程序
strip部分

strip 函数是一个用于处理字符串的函数。它主要用于去除字符串的空格或特定字符,常用于构建或定义变量时进行字符串清理。
语法

strip(param)

参数: param 表示要处理的输入字符串。

返回值: 返回去除字符串两端空白字符后的字符串。

功能
strip 函数删除输入字符串开头和结尾的空格(包括制表符和换行符),并保留中间的空格。
这个函数通常用于确保字符串在使用前没有多余的空格,避免在构建过程中出现意外的格式问题。

案例

MY_VAR :=    Hello, World!   
STRIPPED_VAR := $(strip $(MY_VAR))

本身是前面有空格的 Hello, World! ,经过strip后没有空格直接输出Hello, World!

filter和filter-out部分

filter 函数用于从一组字符串中筛选出符合特定模式的字符串。这对于处理和管理编译文件或目标文件非常有用。
语法

filter(PATTERN1 PATTERN2 ... , STRING1 STRING2 ...)

参数:
PATTERN1 PATTERN2 …:一系列模式,可以使用通配符(如 * 和 ?)。
STRING1 STRING2 …:要筛选的字符串列表。
返回值: 返回与所有模式匹配的字符串。
功能
filter 函数遍历指定的字符串列表,并返回所有匹配提供模式的字符串。
如果没有任何字符串与模式匹配,返回空字符串。

案例

#案例1
FILES := file1.o file2.o file3.o fileA.o fileB.o  
# 筛选出以 file1 和 fileA 开头的文件  
FILTERED_FILES := $(filter file1.o fileA.o, $(FILES))
#案例2
FILES := src/file1.cpp src/file1.h src/file2.cpp src/fileA.java  
# 筛选出以 file1 结尾的文件  
FILTERED_FILES := $(filter %file1.*, $(FILES))

filter-out
作用其实和filter恰恰相反,filter-out函数用于从一组字符串中删除特定模式的字符串。
语法

$(filter-out key1 key2,$(VAR))

把VAR中包含的key1和key2过滤掉,其余的全部保留。
对比案例如下:

1.如:VAR = a  b  c d,  key1=a,key2=b

则:

$(filter key1 key2,$(VAR))    ===> a b

$(filter-out key1 key2,$(VAR)) ====>c d 

 

2.如:VAR = a  b  c d,  key1=a,key2=e

则:

$(filter key1 key2,$(VAR))    ===> a

$(filter-out key1 key2,$(VAR)) ====> b c d
ifeq 和 ifneq

ifeq 和 ifneq 是在 Makefile 中用于条件判断的指令,它们允许根据给定条件执行不同的代码块。这两个指令主要用于根据变量的值执行不同的操作。

ifeq语法

ifeq (条件1, 条件2)  
    # 条件成立时执行的代码  
endif

案例

VERSION := 1.0  
ifeq ($(VERSION), 1.0)  
    $(info Version is 1.0)  
endif

ifneq语法

ifneq (条件1, 条件2)  
    # 条件不成立时执行的代码  
endif

案例

VERSION := 1.0  
ifneq ($(VERSION), 2.0)  
    $(info Version is not 2.0)  
endif

其他更多mk相关可以看官网:
https://developer.android.google.cn/ndk/guides/android_mk#local_src_files

更多framework实战开发干货,请关注下面“千里马学框架”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值