目录
前言 —— 需求提出
Android 8.0- —— Android.mk实现条件编译
Android 9.0+ —— Android.bp实现条件编译
扩展:Android 9.0+ —— Android.bp实现自定义条件编译
本人正在学习中。此篇文章如有不正之处,欢迎指正讨论!
最近在协助完成模块优化的时候,有个需求如下:
依赖结构:A B C……等多个平台共用一个自定义系统模块
随后优化此模块中内容,改动需要跟随A平台进行系统迭代测试,但希望BC等其他平台不受此优化内容影响
基于上面的需求,整理了一下条件:
1.平台系统版本包括Android 8.0、9.0。
2.自定义系统模块功能已经在源码打点完毕,尽可能少地改动系统源码(否则要多个平台都修改)。
3.不同平台可快速进行优化方案和非优化方案的切换,便于在不同阶段的各个平台能不受影响快速切换方案。
因此想出了以下做法:
1.保证优化后的类名和包名一致,可以放到包名以上的其他目录中。 —— 确保系统源码中的打点无需改动。
如自定义系统模块中优化前的类和包名为:(module_path)/hotkey/service/core/java/com/android/server/policy/HotKey.java
自定义系统模块中优化后的类和包名为:(module_path)/audio/service/core/java/com/android/server/policy/HotKey.java
如此一来,HotKey的类名和包名与原来一致,系统中引用到HotKey的地方依旧只需要import com.android.server.policy.HotKey; 无需变动。
2.在Android.mk和Android.bp中完成条件编译。 —— 兼容不同版本系统平台,根据条件快速切换优化方案。
下面就想方法解决这个问题吧!
先给出一份8.0系统framework下的mk文件(framework/base/service/core):
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := services.core
……
LOCAL_SRC_FILES += \
$(call all-java-files-under,java) \
java/com/android/server/EventLogTags.logtags \
java/com/android/server/am/EventLogTags.logtags \
../../../../system/netd/server/binder/android/net/INetd.aidl \
../../../../system/netd/server/binder/android/net/metrics/INetdEventListener.aidl \
../../../native/cmds/installd/binder/android/os/IInstalld.aidl \
# patch start
$(custom_module_files) \
# patch end
……
include $(BUILD_STATIC_JAVA_LIBRARY)
在源码中mk中加入 $(module_files),在自定义系统模块的mk中完成 module_files 的定义,将改动尽量放到系统自定义模块下。
Android.mk不必多说,通过在mk里设立变量,即可使用ifeq、ifdef等判断值,进行条件编译。如定义:
OPTIMIZE := true
随后修改mk参与编译的文件,如下:
custom_module_files := \
……
# patch start
ifeq ($(OPTIMIZE),true)
custom_module_files += \
(custom_module_path)/audio/services/core/java/com/android/server/policy/HotKey.java
else
custom_module_files += \
(custom_module_path)/hotkey/services/core/java/com/android/server/policy/HotKey.java
endif
# patch end
其中audio目录下的HotKey.java是优化后待测试的代码,而policy目录下的HotKey则是未优化的稳定代码。通过定义mk中的变量,就可以完成mk文件的条件编译了。
Android在7.0引入 ninja 编译系统,8.0引入Android.bp替代Android.mk,9.0强制使用Android.bp作为编译配置。
解决完8.0的编译问题,准备着手9.0的编译问题了。出于一样的想法,在bp里完成条件编译,然后和mk处理方式一样,通过开关决定参与编译的文件,即可解决此问题。但是Android.bp实际上是一个纯粹的json配置文件,没有条件、分支等流程结构,因此即便使用自带的androidmk工具想要将Android.mk快速转成Android.bp,也会发现流程语句并不起效果。为了解决此问题,google将条件编译进行分家,Android.bp负责纯粹配置,引入Go文件负责进行流程结构判断。
这样一来思路就清楚了:
下面用代码实现:
1.自定义模块custommodule下Android.bp定义两个方案(优化和未优化),如:
# patch start
filegroup {
name: "custom_optimize",
srcs: [
"audio/services/core/java/com/android/server/policy/HotKey.java",
],
}
filegroup {
name: "custom_origin",
srcs: [
"hotkey/services/core/java/com/android/server/policy/HotKey.java",
],
}
# patch end
2.添加用于自定义系统模块条件编译的Go文件。如在自定义模块名字为customsystem,则在customsystem目录下创建customsystem.go,内容如下:
package customsystem
import (
"android/soong/android"
"android/soong/java"
"fmt"
)
//注册module模板,存放默认属性
func init() {
android.RegisterModuleType("custom_optimize_default", custom_optimize_go)
}
func custom_optimize_go() android.Module {
module := java.DefaultsFactory()
android.AddLoadHook(module, custom_optimize_append_properties)
return module
}
func custom_optimize_append_properties(ctx android.LoadHookContext) {
type props struct {
Srcs []string
}
p := &props{}
sdkVersion := ctx.AConfig().PlatformSdkVersionInt()
fmt.Println("sdkVersion:", sdkVersion)
if sdkVersion >=28 { //after P
p.Srcs = append(p.Srcs, ":custom_optimize")
} else {
p.Srcs = append(p.Srcs, ":custom_origin")
}
ctx.AppendProperties(p)
}
3.framework下Android.bp加入自定义模块声明(framework/base/service/core/Android.bp):
# patch start
custom_optimize_default{
name: "custom_optimize_default",
}
# patch end
java_library_static {
name: "services.core.unboosted",
# patch start
defaults: [
"custom_optimize_default",
],
# patch end
aidl: {
……
},
srcs: [
……
],
……
}
这样子就完成了一次条件编译,此处使用的条件是Android版本。如果需要其他变量作为判断条件,可以查看out/soong/soong.variables文件,里面是系统中提供的所有环境变量,如当前系统Android版本、是否调试版本、架构、路径等等:
{
"Platform_version_name": "9",
"Platform_sdk_version": 28,
"Platform_sdk_codename": "REL",
"Platform_sdk_final": true,
"Allow_missing_dependencies": false,
"Debuggable": false,
"Eng": false,
"DeviceArch": "arm",
"DeviceArchVariant": "armv7-a-neon",
"DeviceCpuVariant": "cortex-a9",
"DeviceAbi": ["armeabi-v7a","armeabi"],
"DeviceSecondaryArchVariant": "",
"DeviceSecondaryCpuVariant": "",
"HostArch": "x86_64",
"HostSecondaryArch": "x86",
"HostStaticBinaries": false,
"CrossHost": "windows",
"CrossHostArch": "x86",
"CrossHostSecondaryArch": "x86_64",
……
}
上一部分做了一个用系统内环境变量条件编译的实例,不过等等,好像和一开头的需求不是很符合?
想一想,仅用sdkVersion进行判断,那么对于所有Android9.0的平台来说只能做统一处理,连区分平台都做不到,更何况区分同code的不同平台了。
那么扩展场景来了:如何打入一个系统环境变量,并通过mk文件赋值,让我们的条件编译真正按照平台生效呢?
翻了资料,全程耗时一周,打通了一条路,原理剖析后续补上,直接上code。(具体可以戳传送门瞅瞅这位大大写出来的通路图 --> https://blog.csdn.net/u012188065/article/details/89352166)
1.在平台mk中,定义mk变量。(此处平台mk选择的是BoardConfig.mk, 路劲device/(厂商)/(平台)/BoardConfig.mk
# patch start
OPTIMIZE := true
# patch end
2.新增bool类型go变量。路径build/soong/android/variable.go
type productVariables struct {
……
// patch start
Optimize *bool `json:",omitempty"`
// patch end
}
3.建立go变量和mk变量的json映射关系,让go变量可以获取到mk中定义的变量值。路径build/make/core/soong_config.mk
……
# patch start
$(call add_json_bool, Optimize, $(filter true, $(OPTIMIZE)))
# patch end
# 这句等同于:
# bool Optimize = (OPTIMIZE == true)
……
4.定义go方法,返回go变量的值。路径:build/soong/android/config.go
……
# patch start
func (c *deviceConfig) Optimize() bool {
return Bool(c.config.productVariables.Optimize)
}
# patch end
……
完成了以上四步后,mk变量就成功映射到go文件中,并生成系统环境变量。
在编译之后可以查看out/soong/soong.variables文件,确认是否生成环境变量,赋值是否正确。
基于上一部分的成果,我们来修改一下实现,完成我们的需求:
修改customsystem.go,如下:
……
func custom_optimize_append_properties(ctx android.LoadHookContext) {
type props struct {
Srcs []string
}
p := &props{}
// 修改成自定义条件
optimize := ctx.DeviceConfig().Optimize()
fmt.Println("mk OPTIMIZE:", optimize)
if optimize {
p.Srcs = append(p.Srcs, ":custom_optimize")
} else {
p.Srcs = append(p.Srcs, ":custom_origin")
}
ctx.AppendProperties(p)
}
……
修改到这里,就完全结束了。现在只需要编译看看,验证效果。
相关原理剖析会另起一篇,未完待续……
——————————————————————————————————————
参考资料:
① 少侠的崛起 - 产品.mk控制android.bp选择性编译 - https://blog.csdn.net/u012188065/article/details/89352166
② 秋少 - Android系统开发进阶-Android编译系统介绍 - https://qiushao.net/2020/02/06/Android%E7%B3%BB%E7%BB%9F%E5%BC%80%E5%8F%91%E8%BF%9B%E9%98%B6/Android%E7%BC%96%E8%AF%91%E7%B3%BB%E7%BB%9F%E4%BB%8B%E7%BB%8D/
(③ 感谢海思的小抄!