1. Android.bp 文件介绍
Android.bp 文件是 Android 系统的一种编译配置文件,用来代替原来的 Android.mk 文件。在 Android7.0 以前,Android 使用 make 来组织各模块的编译,对应的编译配置文件是 Android.mk 文件。Android7.0 开始,Google 引入了 Ninja 和 Kati 来编译,Ninja 的配置文件就是 Android.bp,Android 系统使用 Blueprint 和 Soong 工具来解析 Android.bp 转换生成 ninja 文件。为了兼容老的 mk 配置文件,Android 也开发了 Kati 工具来转换 mk 文件生成 ninja。Blueprint 和 Soong 都是由 Golang 写的项目,从 Android 7.0,prebuilts/go/ 目录下新增 Golang 所需的运行环境,在编译时使用。bp 跟 mk 文件不同,它是纯粹的配置,没有分支、循环等流程控制,不能做算数逻辑运算。如果需要控制逻辑,那么只能通过 Go 语言编写。
2. Android.bp 语法规则
Android.bp 示例如下:
/packages/apps/QuickAccessWallet/Android.bp
go
android_app {
name: "QuickAccessWallet",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
platform_apis: true,
system_ext_specific: true,
certificate: "platform",
privileged: true,
manifest: "AndroidManifest_App.xml",
static_libs: [
"SystemUIPluginLib",
"androidx.cardview_cardview",
"androidx.recyclerview_recyclerview",
],
optimize: {
proguard_flags_files: [
"proguard.flags",
],
},
}
Android.bp 的语法结构与 JSON 的语法结构相似,都是以键值对的形式编写:
go
[module type] {
name: ["name value"],
[property1name]: ["property1value"]
[property2name]: ["property2 value"]
}
常见的 module type 类型:
编译成 jar
go
Android.bp
java_library {
......
}
编译成 aar
go
android_library {
......
}
编译成 apk
go
android_app {
......
}
编译成 Native 动态库
go
cc_library_shared {
......
}
编译成 Native 静态库
go
cc_library_static {
......
}
常见的 property 类型:
模块命名(模块命名是唯一的!)
go
name: ......
指定当前模块的源码位置,可使用通配符 *
go
srcs: [......]
指定当前模块的资源文件位置
go
resource_dirs: [......]
指定AndroidManifest.xml文件位置
go
manifest: ......
指定 aidl 文件位置
go
aidl: {
local_include_dirs: [......],
}
允许使用系统 API
go
platform_apis: true
使用系统签名
go
certificate: "platform"
设定 apk 安装路径为 priv-app
go
privileged: true
引入依赖库
go
static_libs: [
......
]
引入编译时依赖库,依赖仅参与编译,不会被打包
go
libs: [
......
]
是否启用代码优化
go
optimize: {
enabled: false,
}
是否预先生成 dex 文件,默认为 true。该属性会影响应用的首次启动速度以及 Android 系统的启动速度
go
dex_preopt: {
enabled: false,
}
注解处理器
go
plugins: [
......
]
编译参数
go
cflags: [
......
]
cppflags: [
......
]
javacflags: [
......
]
kotlincflags: [
......
]
certificate 用于指定 apk 的签名方式,Android 中共有四中签名方式
- testkey:普通 apk ,则默认使用该签名
- platform:如果 apk 需要对系统中存在的文件夹进行访问等,或者需要完成一些系统的核心功能,则使用改签名,这种方式编译出来的 apk 所在进程的 uid 为 system
- shared:如果 apk 需要和 home/contacts 进程共享数据,则使用该签名
- media:如果 apk 是 media/download 系统中的一环,则使用该签名
指定源文件位置,可以用通配符指定 src/main/java 目录下,所有后缀是 .java 和 .kt 的文件
go
srcs: ["src/main/java/**/*.java", "src/main/java/**/*.kt"],
指定 manifest
go
manifest: "src/main/AndroidManifest.xml",
设置允许使用系统 API
go
platform_apis: true,
PS:如果想了解更多的模块类型和属性,可以通过out/soong/docs/中的soong_build.html文件来查阅~
3.Androidmk 工具介绍
Androidmk是soong中集成的一个小工具。作用是将android.mk文件转换成android.bp文件。
用法如下:
- 整体编译代码
- 编译成功后,在out/soong/host/linux-x86/bin/下会有一个androidmk文件(通过它才能进行mk->bp转换)
- 把想要转换的.mk文件放到out/soong/host/linux-x86/bin/ 路径下
- 输入命令:
androidmk Android.mk > Android.bp
其中 Android.mk 是你想要转换的.mk文件。 Android.bp是生成的对应.bp文件
例子:以CarStateService下.mk为例子,给大家排一下槽点..
CarStateService中Android.mk如下:
转换后生成的Android.bp文件如下:
红框圈出来的都是转换过程中androidmk不识别的部分,会自动给注释掉。
总结:
- androidmk工具只能转换"较为简单的".mk文件(这里的简单是指:自定义的属性少、没有分支、循环等流程控制)
- androidmk工具的使用是依赖out/soong/host/linux-x86/bin/路径下的生成物的(必须要有编译产物androidmk)
我踩过的坑,以及如何规避:
①如果在out/soong/host/linux-x86/bin/路径下执行了转换命令后,Android.bp文件是空的怎么办?
解决方式:建议make clean 重新整编代码。
②转换后部分属性未识别到,被androidmk注释掉了(例如上图中红框圈出的LOCAL_PRIVATE_PLATFORM_APIS),怎么能找到该属性在bp文件中的正确写法?
解决方式:源码查阅网址:aospxref.com/android-12....
这里面记录了.mk文件属性对应在.bp文件中怎么定义,如下图(以.mk中LOCAL_PRIVATE_PLATFORM_APIS属性为例子)
4.格式规范
- 4个空格缩进。
- 多元素列表的每个元素后的换行符。
- 在lists和maps的结尾处始终包含一个逗号。
bpfmt工具:也可以使用bpfmt工具来规范.bp文件的格式。例如:要格式化当前目录中所有Android.bp文件
可以输入
bpfmt -w .
(.前边有空格!)
5.条件编译如何添加?-> .go文件
由于Android.bp文件是 纯 配 置 文 件,条件编译控制则依赖.go文件
下面以一例简单的需求,来举例.go文件的编写,及Android.bp文件如何与其建立关系,从而实现条件编译。
需求:根据TARGET_PRODUCT来进行条件编译,如果TARGET_PRODUCT=test那么引入相关宏,否则打印调试信息。
.go文件如下:
go
//包名自拟,建议和文件名相同
package testparser
import (
//引入编译依赖
"android/soong/android"
"android/soong/cc"
//引入调试信息
"fmt"
)
func init() {
//输出调试信息
fmt.Println("test init ->>>>")
//注册模块
android.RegisterModuleType("linker_test", testDefaultsFactory)
}
func testDefaultsFactory() (android.Module) {
//获取模块对象
fmt.Println("test testDefaultsFactory->>>>")
module := cc.DefaultsFactory()
android.AddLoadHook(module, testAndroidDefaults)
return module
}
func testAndroidDefaults(ctx android.LoadHookContext) {
fmt.Println("test testAndroidDefaults->>>>")
type props struct {
Cflags []string
}
p := &props{}
//获取差分后的属性
p.Cflags = globalDefaults(ctx)
ctx.AppendProperties(p)
}
func globalDefaults(ctx android.BaseContext) ([]string) {
//声明了一个切片(数组)
var cppflags []string
//条件逻辑在这里
fmt.Println("test globalDefaults ->>>>")
if ctx.AConfig().Getenv("TARGET_PRODUCT") == "test" {
//将属性加入cppflags中
cppflags = append(cppflags,"相关宏")
} else {
fmt.Println("如果项目的target不为test则输出日志")
}
//最终cppflags会被 p.Cflags引用,通过ctx.AppendProperties(p)加入到编译环境中
return cppflags
}
Android.bp文件进行引用:
arduino
//模块类型选择bootstrap_go_package,用于编译Go文件资源
bootstrap_go_package {
name: "soong_testparser_defaults",
//指定引用路径
pkgPath:"vendor/fulscience/packages/services/CarStateService/service",
// 添加你对应的Go 文件资源
srcs: [
"testparser.go",
],
//添加编译依赖
deps:[
"soong-android",
"soong-cc",
"soong",
"soong-genrule",
"blueprint",
"blueprint-pathtools",
],
pluginFor:["soong_build"],
}
//自定义了模块类型,该模块类型会被go文件中的RegisterModuleType注册,从而建立连接关系
linker_test {
//模块名称
name :"testparser_defaults",
}
android_app {
name: "CarStateManagerService",
srcs: [
"java/**/*.java"
],
resource_dirs: [
"res"
],
platform_apis:true,
certificate: "platform",
privileged:true,
optimize: {
enabled: false,
},
libs: [
"android.car",
"dsv-platformadapter",
],
static_libs: [
"android.hardware.automotive.vehicle-V2.0-java",
"com.desaysv.vehiclelan.proxy-V1.0-java",
"android.hardware.dsp-V1.0-java",
"vehicle-hal-support-lib",
"androidx.annotation_annotation",
"android-support-constraint-layout-solver",
"jsr305",
"android-support-constraint-layout",
],
//引入自定义模块的名称
defaults :[
"testparser_defaults",
]
}
到此为止Android.bp文件就已经成功的引用了.go文件。
ps:仅供参考,一切以实际情况为准~