在实际开发过程中,经常会遇到通过 宏/环境变量 判断是否加载某个模块,这时就需要进行条件编译。
Android 7.0 前使用 Android.mk 来编译模块,makefile
支持条件编译;但是 Android 7.0 之后逐步被 Android.bp 取代,Android.bp 本质上是 json
配置文件,不支持条件判断,所以 Google 提供了通过 go 语言来实现 Android.bp 的条件编译。
相关的的官方描述:Soong 编译系统
Android.bp语法
模块
Module | Desc |
---|---|
cc_defaults | 父模块,可以通过 defaults 属性被其它模块继承 |
cc_library | 编译成库文件 |
cc_binary | 编译成可执行文件 |
属性
Attribute | Desc |
---|---|
name | 模块名 |
defaults | 继承父模块 |
vendor/proprietary | 编译到 /vendor 分区 |
shared/static_libs | 依赖的共享库 .so 以及静态库 .a |
Android.bp 条件编译实例
以 helloworld 为例,文件结构
1
2
3
4
5
6
7
8
9
10
11
➜ helloworld git:(master) ✗ tree .
.
├── Android.bp
├── HelloWorld.cpp
├── include
│ └── HelloWorld.h
└── soong
├── Android.bp
└── HelloWorld.go
2 directories, 5 files
1 helloworld/Android.bp 配置编译模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cc_helloworld_defaults {
name: "helloworld-defaults",
}
cc_library {
name: "helloworld",
defaults: ["helloworld-defaults"],
vendor_available: true,
src: ["helloworld.cpp"],
Local_include_dirs: ["include"],
shared_libs: ["liblog"],
static_libs: ["hello"],
}
helloworld/Android.bp
定义了一个 cc_helloworld_defaults
模块 helloworld-defaults
,这个模块类型在 helloworld.go
中注册。
我们目的编译的 helloworld
模块,通过 defaults
继承了 helloworld-defaults
的配置,这样通过 helloworld.go
实现的条件编译就附加到了helloworld
模块上,从而达到条件编译的目的。
2 helloworld/soong/Android.bp 配置 soong
1
2
3
4
5
6
7
8
9
10
bootstrap_go_package {
name: "soong-helloworld",
pkgPath: "android/soong/hello",
deps: [
"soong-android",
"soong-cc",
],
srcs: ["helloworld.go"],
pluginFor: ["soong_build"],
}
soong/Android.bp
添加了一个 bootstrap_go_package
模块 soong-helloworld
,指定源文件为 helloworld.go
,在解析这个 Android.bp
时,会对 helloworld.go
进行编译。
3 helloworld/soong/helloworld.go 添加hook
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package hello
import (
"android/soong/android"
"android/soong/cc"
"fmt"
"strconv"
)
func helloworldHook(ctx android.LoadHookContext) {
//AConfig() function is at build/soong/android/config.go
fmt.Println("PlatformSdkVersion = ", ctx.AConfig().PlatformSdkVersion())
fmt.Println("DeviceName = ", ctx.AConfig().DeviceName())
type props struct {
Cflags []string
}
p := &props{}
p.Cflags = append(p.Cflags, "-DPLATFORM_SDK_VERSION=" + ctx.AConfig().PlatformSdkVersion())
ctx.AppendProperties(p)
}
func init() {
android.RegisterModuleType("cc_helloworld_defaults", helloDefaultsFactory)
}
func helloDefaultsFactory() (android.Module) {
module := cc.defaultsFactory()
android.AddLoadHook(module, helloHook)
return module
}
init
函数会先执行, 在 init
里面注册了一个新的模块类型 cc_helloworld_defaults
, 对应的函数是 helloDefaultsFactory
。 需要注意的是其中 cc.DefaultsFactory
要根据模块类型的不同而不同,
cc_binary
–>cc.DefaultsFactory()
cc_library_shared
–>cc.LibrarySharedFactory()
java_library
–>java.LibraryFactory()
这个可以在 build/soong/cc/library.go
和 cc.go
中查看, 比如 library.go
中,
1
2
3
4
5
6
7
8
9
10
func init() {
RegisterLibraryBuildComponents(android.InitRegistrationContext)
}
func RegisterLibraryBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("cc_library_static", LibraryStaticFactory)
ctx.RegisterModuleType("cc_library_shared", LibrarySharedFactory)
ctx.RegisterModuleType("cc_library", LibraryFactory)
ctx.RegisterModuleType("cc_library_host_static", LibraryHostStaticFactory)
ctx.RegisterModuleType("cc_library_host_shared", LibraryHostSharedFactory)
}
然后我们给 cc_helloworld_defaults
模块类型添加了一个 hook:helloworldHook
,当有 cc_helloworld_defaults
类型的模块定义时, helloworldHook
就会被触发执行。
条件编译就是在这个 hook 里面进行,可以通过 ctx
来获取各种编译信息, 比如 PlatformSdkVersion
, DeviceName
。
我们可以通过 ctx.AppendProperties
来添加各种配置,可以配置的东西如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
type props struct {
Cflags []string
Srcs []string
Include_dirs []string
Shared_libs []string
Local_include_dirs []string
Static_libs []string
Export_shared_lib_headers []string
}
// StaticOrSharedProperties is an embedded struct representing properties to affect attributes of
// either only the "static" variants or only the "shared" variants of a library module. These override
// the base properties of the same name.
// Use `StaticProperties` or `SharedProperties`, depending on which variant is needed.
// `StaticOrSharedProperties` exists only to avoid duplication.
type StaticOrSharedProperties struct {
// 定义Android.bp中的各个字段
Srcs []string `android:"path,arch_variant"`
Tidy_disabled_srcs []string `android:"path,arch_variant"`
Tidy_timeout_srcs []string `android:"path,arch_variant"`
Sanitized Sanitized `android:"arch_variant"`
Cflags []string `android:"arch_variant"`
Enabled *bool `android:"arch_variant"`
Whole_static_libs []string `android:"arch_variant"`
Static_libs []string `android:"arch_variant"`
Shared_libs []string `android:"arch_variant"`
System_shared_libs []string `android:"arch_variant"`
Export_shared_lib_headers []string `android:"arch_variant"`
Export_static_lib_headers []string `android:"arch_variant"`
Apex_available []string `android:"arch_variant"`
Installable *bool `android:"arch_variant"`
}
helloworld.go
中我们只是添加了一个 Cflags 而已, 如果需要的话,我们可以添加源码文件及头文件目录,及依赖等。
4 使用传进来的宏
1
2
3
4
5
6
7
#include <cstdio>
int main() {
printf("helloworld\n");
printf("PLATFORM_SDK_VERSION = %d\n", PLATFORM_SDK_VERSION);
return 0;
}
5 编译验证
1
2
3
$ helloworld
helloworld
PLATFORM_SDK_VERSION = 31
references: