为了降低工作量以及考虑到产品的可维护性,大部分手机厂商采用了同一套android代码对应多个产品,编译时根据配置参数选择性编译不同模块的代码。
1、 Android.mk时代选择性编译
最初android的编译脚本为 Android.mk,采用了Makefile的语言
为了控制编译,我们可以在不同产品的BoardConfig.mk中定义 MARCO
之后在Android.mk中,根据是否使用MARCO来确定编译选项以及编译代码块
ifeq ($(MARCO),true)
xxxx
endif
2、Android.bp时代选择性编译
随着android逐渐将编译脚本从Android.mk转换成Android.bp,这种方法不再有效,
我们需要采用一种新的方式来选择性编译,此时使用到了go语言。
假如在vendor/privateParser/ 目录下有我们自己的解析器代码,多个版本多个平台均会使用。
在 android 9.0( SDK: 28) 及之后的平台,加载解析器的方式发生了改变,
MediaExtractorFactory会从 手机的 system/lib(64)/extractor/目录下加载.so,
需要将此.so编译到 system/lib/extractors/目录下
之前的平台编译到system/lib/ 目录下
参考google的MPEG4Extractor的代码,需要在 Android.bp中增加如下语句:
relative_install_path: "extractors",
才能保证编译出的.so 位于system/lib(64)/extractor/目录下
由于我们的代码是共线的,需要保证9.0的平台有此编译选项,之前的平台没有此编译选项
由于不能通过android.bp来区分平台,我们需要增加 go 脚本,来保证此编译选项
2.1、增加go脚本
vendor/privateParser/ 目录下增加go脚本 privateparser.go, 如下:
package privateparser
import (
"android/soong/android"
"android/soong/cc"
"fmt"
)
func privateParserDefaults(ctx android.LoadHookContext) {
sdkVersion := ctx.AConfig().PlatformSdkVersionInt()
fmt.Println("sdkVersion:", sdkVersion)
if sdkVersion >=28 { //after P
type props struct {
Shared_libs []string
Relative_install_path *string
}
p := &props{}
var sharedlib []string
sharedlib = append(sharedlib, "libmediaextractor")
p.Shared_libs = sharedlib
relative_install_path := "extractors"
p.Relative_install_path = &relative_install_path
ctx.AppendProperties(p)
}
}
func init() {
android.RegisterModuleType("privateparser_defaults", privateParserDefaultsFactory)
}
func privateParserDefaultsFactory() android.Module {
module := cc.DefaultsFactory()
android.AddLoadHook(module, privateParserDefaults)
return module
}
2.2、android.bp使用此脚本
android.bp开头位置增加如下语句
privateparser_defaults {
name: "privateparser_defaults",
}
bootstrap_go_package {
name: "soong-privateparser",
pkgPath: "android/soong/external/privateParser",
deps: [
"blueprint",
"blueprint-pathtools",
"soong",
"soong-android",
"soong-cc",
"soong-genrule",
],
srcs: [
"privateparser.go",
],
pluginFor: ["soong_build"],
}
以上语句可以保证运行android.bp时,先编译对应的 privateparser.go
运行go脚本时,会首先运行init函数,将 privateParserDefaultsFactory 函数注册到module中
之后调用privateParserDefaultsFactory函数时,会将回调函数 privateParserDefaults 注册进去
之后调用 privateParserDefaults 时,我们可以从 ctx.AConfig() 中获取好多属性
(参考 build/soong/android/config.go 中对 build/soong/android/module.go中的 androidBaseContext interface的各种函数实现),其中有一项是获取 sdk版本号,根据版本号,来控制编译选项
之后在 share_libs 中增加 “libmediaextractor”, relative_install_path 中增加 “extractors”
对应 go脚本中的变量名为 Share_libs 和 Relative_install_path
注1: go语言中的变量名,可以在 build/soong/androidmk/cmd/androidmk/android.go 的init函数中查看map表
注2: 必须在确定sdk版本号后才能确定 type props struct,因为不同的版本号会有不同的变量明
如 Relative_install_path 在sdk 27上的类型为 string, 在 sdk 28上的类型为 * string 类型
至此,运行android.bp时,会先去运行go脚本,将一些某些特定的编译选项加载到此so的整体编译选择中
之后运行到 bp 之后的语句,才会去加载其他编译选择,实现了go脚本选择性编译的结果。
3、 深入解析go脚本
3.1 解析 init 函数
运行go语言时,先运行init函数,此函数为
func init() {
android.RegisterModuleType("privateparser_defaults", privateParserDefaultsFactory)
}
3.1.1 解析RegisterModuleType函数
脚本开头import了两个包 “android/soong/android” 和 “android/soong/cc”
从 build/soong/ 目录下搜索 “func RegisterModuleType” 来获取RegisterModuleType函数的定义位置
RegisterModuleType函数位于build/soong/android/register.go中,如下:
func RegisterModuleType(name string, factory ModuleFactory) {
moduleTypes = append(moduleTypes, moduleType{name, ModuleFactoryAdaptor(factory)})
}
可以看出将 name 和 ModuleFactoryAdaptor(factory) append到 var moduleTypes []moduleType 中
moduleType类型如下:
type moduleType struct {
name string
factory blueprint.ModuleFactory
}
3.1.2 查找ModuleFactory类型
register.go 开头 import “github.com/google/blueprint”
我们从github上下载 github.com/google/blueprint/ 仓后,搜索 “type ModuleFactory” 来获取 ModuleFactory 定义位置,
位于 github.com/buleprint/context.go中,
// A ModuleFactory function creates a new Module object. See the
// Context.RegisterModuleType method for details about how a registered
// ModuleFactory is used by a Context.
type ModuleFactory func() (m Module, propertyStructs []interface{})
可以看出ModuleFactory为一函数指针,形参为null,返回类型为 Module, []interface{},
注1: 此处我们详细的讲解了如何搜索变量名和函数名的位置,即关键字 “type xxx"和"func xxx”,之后不再给出详细步骤
3.1.3 解析ModuleFactoryAdaptor函数
搜索如下:
// ModuleFactoryAdapter Wraps a ModuleFactory into a blueprint.ModuleFactory by converting an Module
// into a blueprint.Module and a list of property structs
func ModuleFactoryAdaptor(factory ModuleFactory) blueprint.ModuleFactory {
return func() (blueprint.Module, []interface{}) {
module := factory()
return module, module.GetProperties()
}
}
将register.go中的 type ModuleFactory func() Module 函数指针,转换成 blueprint.ModuleFactory 类型的函数指针
3.2 解析回调函数 privateParserDefaultsFactory
从上文可以得知,privateParserDefaultsFactory类型为 type ModuleFactory func() Module
我们实现了这个函数为
func privateParserDefaultsFactory() android.Module {
module := cc.DefaultsFactory()
android.AddLoadHook(module, privateParserDefaults)
return module
}
3.2.1 解析 DefaultsFactory()函数
函数实现见 build/soong/cc/cc.go,如下:
func DefaultsFactory(props ...interface{}) android.Module {
module := &Defaults{}
module.AddProperties(props...)
module.AddProperties(
&BaseProperties{},
&VendorProperties{},
&BaseCompilerProperties{},
&BaseLinkerProperties{},
&LibraryProperties{},
&FlagExporterProperties{},
&BinaryLinkerProperties{},
&TestProperties{},
&TestBinaryProperties{},
&UnusedProperties{},
&StlProperties{},
&SanitizeProperties{},
&StripProperties{},
&InstallerProperties{},
&TidyProperties{},
&CoverageProperties{},
&SAbiProperties{},
&VndkProperties{},
<OProperties{},
&PgoProperties{},
&android.ProtoProperties{},
)
android.InitDefaultsModule(module)
return module
}
未完待续……