Android构建系统:Android.mk(2)函数详解

简介: Android构建系统:Android.mk(2)函数详解

在这篇博客中,我将介绍Android.mk详解中的其中一个部分【函数】和make/m等命令概念的区分,Android.mk文件中使用Makefile中函数的概念和用法,以及一些常用的内置函数和自定义函数。通过使用函数,可以简化和优化Makefile中的字符串操作,可以实现一些复杂的功能。我会附上详细的测试代码和测试结果。。

Android构建系统:Android.mk(1)基础概念赋值变量引用详解

Android构建系统:Android.mk(2)函数详解

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

我看到Android.mk里面的函数,最开始概念模糊的时候,闪过一个疑问:

Android.mk 为什么能使用Makefile函数?

查阅资料了解到:

需要先搞清楚Android.mkMakefile的差异,Android.mk是早期Android构建系统的一部分,能够执行Android.mk的工具叫makemake是用来搞自动化编译和构建的。那make的可执行对象有哪些呢?第一印象当然是Makefile了,但是查阅资料发现Android.mk 其实是Android构建系统定制的Makefile

可以理解为原厂和ODM(定制)的区别吧?所以可以使用所有 make 提供的标准函数和变量,以及 Android 构建系统提供的一些特定扩展。Android.mk 的构建系统是基于 make 工具的。

测试代码和结果

完整的测试结果:

完整的测试代码:

$(warning ====== 测试开始 ======)
#LOCAL_PATH := $(call my-dir)
# 使用subst 函数
# subst 函数用于替换字符串中的某些字符
# 它接受三个参数,分别是要替换的字符、替换后的字符和原始字符串
# 它会返回替换后的字符串
$(info [SUBST] $(subst a,b,cat) <- (cat: a -> b)) # 将cat 中的a 替换为b,结果为cbt
$(info [SUBST] $(subst .c,.o,foo.c) <- (foo.c: .c -> .o)) # 将foo.c 中的.c 替换为.o,结果为foo.o
# 使用patsubst 函数
# patsubst 函数用于根据模式替换字符串中的某些字符
# 它接受三个参数,分别是要替换的模式、替换后的模式和原始字符串
# 它会返回替换后的字符串
# 模式中可以使用% 表示任意长度的字符
# 将.c 替换为.o,结果为foo.o bar.o baz.o
$(info [PATSUBST] $(patsubst %.c,%.o,foo.c bar.c baz.c) <- (%.c -> %.o)) 
# 将foo 开头的字符串替换为bar 开头的字符串,结果为barbar barbaz barqux
$(info [PATSUBST] $(patsubst foo%,bar%,foobar foobaz fooqux) <- (foo% -> bar%)) 
# 使用strip 函数
# strip 函数用于去除字符串中的多余空格
# 它接受一个参数,即原始字符串
# 它会返回去除空格后的字符串
$(info [STRIP] "$(strip   hello   example02  )" <- 去除多余空格) # 去除多余空格,结果为hello example02
# 使用wildcard 函数
# wildcard 函数用于根据模式匹配文件名
# 它接受一个参数,即匹配模式
# 它会返回匹配到的文件名列表
# 模式中可以使用* 表示任意长度的字符,? 表示任意单个字符,[...] 表示任意一个在括号内的字符
# 匹配所有以.c 结尾的文件名,结果为foo.c(假设当前目录下只有这个文件)
$(info [WILDCARD] $(wildcard *.c) <- 匹配所有以.c 结尾的文件名) 
# 匹配所有以foo 开头,一个任意字符,.c 结尾的文件名,结果为空(假设当前目录下没有这样的文件)
$(info [WILDCARD] $(wildcard foo?.c) <- 匹配所有以foo 开头,一个任意字符,.c 结尾的文件名) 
# 匹配所有以foo 开头,a 或b 或c,.c 结尾的文件名,结果为空(假设当前目录下没有这样的文件)
$(info [WILDCARD] $(wildcard foo[abc].c) <- 匹配所有以foo 开头,a 或b 或c,.c 结尾的文件名) 
# 使用foreach 函数
# foreach 函数用于对列表中的每个元素执行某个操作
# 它接受三个参数,分别是变量名、列表和操作表达式
# 它会返回操作后的结果列表
# 对每个元素乘以2,并打印结果
$(info [FOREACH] $(foreach x,2 0 2 3 ,$(x) * 2 = $(shell expr $(x) \* 2))) 
# 使用if 函数
# 如果Ln28 已经定义,打印Ln28 is defined,否则打印Ln28 is not defined
$(info $(if $(Ln28),Ln28 is defined,Ln28 is not defined)) 
# 如果FILE 是一个以.c 结尾的文件名,打印FILE is a C source file,否则打印FILE is not a C source file
FILE := fooa.c
$(info $(if $(filter %.c,$(FILE)),$(FILE) is a C source file,$(FILE) is not a C source file)) 
# 定义一个自定义函数,用于计算两个数的和
define add
$(shell expr $(1) + $(2))
endef
# 使用call 函数调用自定义函数
$(info call add:$(call add,2,0)) # 调用add 函数,传递2 和0 作为参数,结果为2
$(info call add:$(call add,2,3)) # 调用add 函数,传递2 和3 作为参数,结果为5
# 定义一个自定义函数,用于给字符串加前缀
define addprefix 
$($(1) + $(2))
endef
# 调用自定义addprefix 函数,给world 和example02前面加上hello ,结果为hello world hello example02
$(info $(addprefix hello ,world example02)) 
$(warning ====== 测试结束 ======)
all:
  @echo "测试example02结束!"

make和m,mm,mmm概念区分

首先我们看到Android.mk 是不是立马想到了(如 mmmmmm 等)等命令来执行?

但不然,我们可以使用 make -f Android.mk 命令的目的是告诉 make 工具使用指定的 Android.mk 文件作为其构建规则文件,而不是默认的 Makefile 文件,为什么要使用make -f ,而不是make?

因为make 是一个构建工具,它使用特定格式的文件(基本上名字是 Makefile)来确定如何构建项目。这个文件包含了构建规则、目标和依赖关系。

-f 选项允许指定一个不同的文件作为 make 的输入。加f-f的意思是多个构建文件或不使用默认名称 Makefile 的情况下。

ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk/vendor/customize/demo/example02$ make 
make: *** 没有指明目标并且找不到 makefile。 停止。
#### failed to build some targets  ####
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk/vendor/customize/demo/example02$ mv Android.mk makefile
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk/vendor/customize/demo/example02$ make 
makefile:1: ====== 测试开始 ======
。。。。省略了,就是完整的测试结果的打印
makefile:71: ====== 测试结束 ======
测试example02结束!
#### build completed successfully  ####

make -f Android.mk 命令的作用是使用 Android.mk 文件作为构建规则来运行 make,但在实际的 Android 构建过程中,这不是常用的命令只是为了验证Makefile的一些内容。

那么什么时候使用 make 什么时候时候(如 mmmmmm 等)等命令来执行?

其实可以这样简单快速区分(但不完全是这么理解啊),

刚开始理解,死记构建一个目标模块用( mmmmmm ),这个目标比如是指include $(BUILD_EXECUTABLE),在我example01的测试代码中,这个就是为了构建一个exec01可执行文件,可执行文件就是一个目标!

小节一下:

  1. make:
  • make 是一个通用的构建工具,用于处理 makefile 文件中定义的构建规则。
  • 在 Android 构建系统中,源码根目录直接运行 make 会根据顶层 Makefile(和其他相关的 *.mk 文件)构建整个 Android 系统,如果在 example02 目录下运行 make 则会找 example02 目录下的 makefile ,如果 make -f Android.mk ,则是指定一个运行的名字。
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk$ make
  1. m:
  • 这是一个快捷命令,等同于在 源代码的根目录下运行 make
  • 它用于构建整个 Android 系统。
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk$ m #m == make
  1. mm:
  • 这个命令专门用于构建当前目录下的模块。
  • 它是为了方便在特定目录下快速构建和测试模块。
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk/vendor/customize/demo/example02$ mm
  1. mmm:
  • 这个命令用于构建指定目录下的所有模块。
  • 它允许指定一个目录并构建该目录下的所有模块。
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk$ mmm vendor/customize/demo/example02/

上面反复加强记忆,那么现在可以这样理解:

  • make 是一个通用的构建工具,它根据 Makefile 或其他指定的文件中的规则来执行构建。在 Android 构建系统中,它通常用于构建整个系统。
  • m, mm, 和 mmm 是 Android 构建系统提供的快捷命令,更有针对性地构建模块。这些命令背后仍然是 make 工具,但这些命令的作用是提供了一个更简洁的方式来指定构建目标的方式。

Makefile中的函数

在Makefile中有时(特别是大型项目,文件特别多的那种,需要快速适配写出一个能自动化构建的文件)需要对一些字符串进行操作,例如替换、匹配、去除空格等。为了方便这些操作,Makefile提供了一些内置的函数,它们可以接受参数并返回结果。除了内置函数,还可以自己定义一些函数,以实现一些特定的功能。

这就是本文重点,将介绍Makefile中函数的使用方法和一些常用的函数。

函数的一般形式

函数是一种特殊的变量,它可以接受参数并返回结果。函数的一般形式为:

$(函数名 参数1,参数2,...)

函数名是一个已经定义好的函数,参数是一个或多个以逗号分隔的表达式,函数会根据参数计算并返回结果。例如:

# 定义一个自定义函数,用于给字符串加前缀
define addprefix 
$($(1) + $(2))
endef
# 调用自定义addprefix 函数,给world 和example02前面加上hello ,结果为hello world hello example02
$(info $(addprefix hello ,world example02))

内置函数

Makefile中有很多内置函数,它们可以实现一些常见的功能,例如:

  • subst:函数用于替换字符串中的某些字符,它接受三个参数,分别是要替换的字符、替换后的字符和原始字符串,它会返回替换后的字符串。例如:
$(info [SUBST] $(subst a,b,cat) <- (cat: a -> b)) # 将cat 中的a 替换为b,结果为cbt
$(info [SUBST] $(subst .c,.o,foo.c) <- (foo.c: .c -> .o)) # 将foo.c 中的.c 替换为.o,结果为foo.o

输出结果为:

[SUBST] cbt <- (cat: a -> b)
[SUBST] foo.o <- (foo.c: .c -> .o)
  • patsubst:函数用于根据模式替换字符串中的某些字符,它接受三个参数,分别是要替换的模式、替换后的模式和原始字符串,它会返回替换后的字符串。模式中可以使用%表示任意长度的字符。例如:
$(info [PATSUBST] $(patsubst %.c,%.o,foo.c bar.c baz.c) <- (%.c -> %.o)) 
# 将foo 开头的字符串替换为bar 开头的字符串,结果为barbar barbaz barqux
$(info [PATSUBST] $(patsubst foo%,bar%,foobar foobaz fooqux) <- (foo% -> bar%))

输出结果为:

[PATSUBST] foo.o bar.o baz.o <- (%.c -> %.o)
[PATSUBST] barbar barbaz barqux <- (foo% -> bar%)
  • strip:函数用于去除字符串中的多余空格,它接受一个参数,即原始字符串,它会返回去除空格后的字符串。例如:
$(info [STRIP] "$(strip   hello   example02)" <- 去除多余空格) # 去除多余空格,结果为hello example02

输出结果为:

[STRIP] "hello example02" <- 去除多余空格
  • wildcard:函数用于根据模式匹配文件名,它接受一个参数,即匹配模式,它会返回匹配到的文件名列表。模式中可以使用*表示任意长度的字符,?表示任意单个字符,[...]表示任意一个在括号内的字符。例如:
# 匹配所有以.c 结尾的文件名,结果为foo.c(假设当前目录下只有这个文件)
$(info [WILDCARD] $(wildcard *.c) <- 匹配所有以.c 结尾的文件名) 
# 匹配所有以foo 开头,一个任意字符,.c 结尾的文件名,结果为空(假设当前目录下没有这样的文件)
$(info [WILDCARD] $(wildcard foo?.c) <- 匹配所有以foo 开头,一个任意字符,.c 结尾的文件名) 
# 匹配所有以foo 开头,a 或b 或c,.c 结尾的文件名,结果为空(假设当前目录下没有这样的文件)
$(info [WILDCARD] $(wildcard foo[abc].c) <- 匹配所有以foo 开头,a 或b 或c,.c 结尾的文件名)

输出结果为(假设当前目录下有以下文件):

[WILDCARD] foo.c foo1.c fooa.c foo2.c <- 匹配所有以.c 结尾的文件名
[WILDCARD] foo1.c fooa.c foo2.c <- 匹配所有以foo 开头,一个任意字符,.c 结尾的文件名
[WILDCARD] fooa.c <- 匹配所有以foo 开头,a 或b 或c,.c 结尾的文件名
  • foreach:这个函数用于对列表中的每个元素执行某个操作,它接受三个参数,分别是变量名、列表和操作表达式,它会返回操作后的结果列表。例如:
$(info [FOREACH] $(foreach x,2 0 2 3 ,$(x) * 2 = $(shell expr $(x) \* 2)))

输出结果为:

[FOREACH] 2 * 2 = 4 0 * 2 = 0 2 * 2 = 4 3 * 2 = 6
  • if:这个函数用于根据条件执行不同的操作,它接受两个或三个参数,分别是条件表达式、为真时的操作表达式和为假时的操作表达式(可选),它会返回执行后的结果。例如:
# 如果Ln28 已经定义,打印Ln28 is defined,否则打印Ln28 is not defined
$(info $(if $(Ln28),Ln28 is defined,Ln28 is not defined)) 
# 如果FILE 是一个以.c 结尾的文件名,打印FILE is a C source file,否则打印FILE is not a C source file
FILE := fooa.c
$(info $(if $(filter %.c,$(FILE)),$(FILE) is a C source file,$(FILE) is not a C source file))

输出结果为(假设Ln28未定义,FILE 为fooa.c):

Ln28 is not defined
fooa.c is a C source file

自定义函数

除了内置函数,我们还可以自己定义一些函数,以实现一些特定的功能。自定义函数的一般形式为:

define 函数名
函数体
endef

函数名是一个自己取的名字,函数体是一些Makefile语句或表达式,可以使用$(1)$(2)等表示传入的参数。自定义函数可以使用call函数来调用。例如:

# 定义一个自定义函数,用于计算两个数的和
define add
$(shell expr $(1) + $(2))
endef
# 使用call 函数调用自定义函数
$(info call add:$(call add,2,0)) # 调用add 函数,传递2 和0 作为参数,结果为2
$(info call add:$(call add,2,3)) # 调用add 函数,传递2 和3 作为参数,结果为5

输出结果为:

call add:2
call add:5

总结

本文介绍了Makefile中函数的使用方法和一些常用的函数和make,m等命令的意义。通过使用函数,可以简化和优化Android.mk(Makefile)中的字符串操作,并且可以实现一些复杂的功能。Android.mk中还有很多其他的内置函数和自定义函数,有时间会继续测试补充。

相关文章
|
1月前
|
人工智能 搜索推荐 物联网
Android系统版本演进与未来展望####
本文深入探讨了Android操作系统从诞生至今的发展历程,详细阐述了其关键版本迭代带来的创新特性、用户体验提升及对全球移动生态系统的影响。通过对Android历史版本的回顾与分析,本文旨在揭示其成功背后的驱动力,并展望未来Android可能的发展趋势与面临的挑战,为读者呈现一个既全面又具深度的技术视角。 ####
|
29天前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
16天前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
18天前
|
存储 安全 Android开发
探索Android系统的最新安全特性
在数字时代,智能手机已成为我们生活中不可或缺的一部分。随着技术的不断进步,手机操作系统的安全性也越来越受到重视。本文将深入探讨Android系统最新的安全特性,包括其设计理念、实施方式以及对用户的影响。通过分析这些安全措施如何保护用户免受恶意软件和网络攻击的威胁,我们希望为读者提供对Android安全性的全面了解。
|
23天前
|
安全 Android开发 iOS开发
深入探讨Android与iOS系统的差异及未来发展趋势
本文旨在深入分析Android和iOS两大移动操作系统的核心技术差异、用户体验以及各自的市场表现,进一步探讨它们在未来技术革新中可能的发展方向。通过对比两者的开放性、安全性、生态系统等方面,本文揭示了两大系统在移动设备市场中的竞争态势和潜在变革。
|
17天前
|
Java Android开发 开发者
探索安卓开发:构建你的第一个“Hello World”应用
在安卓开发的浩瀚海洋中,每个新手都渴望扬帆起航。本文将作为你的指南针,引领你通过创建一个简单的“Hello World”应用,迈出安卓开发的第一步。我们将一起搭建开发环境、了解基本概念,并编写第一行代码。就像印度圣雄甘地所说:“你必须成为你希望在世界上看到的改变。”让我们一起开始这段旅程,成为我们想要见到的开发者吧!
24 0
|
29天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
1月前
|
缓存 前端开发 Android开发
安卓开发中的自定义视图:从零到英雄
【10月更文挑战第42天】 在安卓的世界里,自定义视图是一块画布,让开发者能够绘制出独一无二的界面体验。本文将带你走进自定义视图的大门,通过深入浅出的方式,让你从零基础到能够独立设计并实现复杂的自定义组件。我们将探索自定义视图的核心概念、实现步骤,以及如何优化你的视图以提高性能和兼容性。准备好了吗?让我们开始这段创造性的旅程吧!
26 1
|
16天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
40 19
|
16天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
41 14