Android系统开发入门-15.Android.bp 条件编译

在工作中我们经常会遇到一套代码需要兼容多个 Android 版本的情况,不同的 Android 版本,代码逻辑可能不一样。我们又不可能每个 Android 版本拉一个代码分支,这样维护成本太高。因此我们一般都是根据 Android 版本进行条件编译。Android 7.0 之前的模块都是使用 Android.mk 来定义的,本质上就是 makefile,自然是支持条件编译的。但 Android7.0 之后,系统模块逐步替换成 Android.bp。但 Android.bp 本质上就是一个 json 配置文件,是不支持条件判断的。但条件编译又是强需求,所以 google 还是提供了一种条件编译的方法,下面我们就来学习一下。

我们的目标是把 platform sdk version 传给一个 Android.bp 模块的 cpp 代码。 直接在之前写过的 hello 模块上进行修改。

1. Android.bp 修改

在 Android.bp 中添加以下配置

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
// add start
bootstrap_go_package {
name: "soong-hello",
pkgPath: "android/soong/hello",
deps: [
"soong-android",
"soong-cc",
],
srcs: [
"hello.go",
],
pluginFor: ["soong_build"],
}

cc_hello_binary {
name: "hello_defaults",
}
// add end


cc_binary {
name: "hello",

// add start
defaults: ["hello_defaults"],
// add end

vendor: true,
srcs: ["hello.cpp"],
}

主要是添加了一个 bootstrap_go_package 模块 soong-hello, 指定源文件为 hello.go, 在解析到这个模块的 Android.bp 时,会对 hello.go 进行编译。
然后我们定义了一个 cc_hello_binary 模块 hello_defaults。这个类型是我们在 hello.go 中定义的。
最后我们指定了 hello 模块的 defaults 为 hello_defaults, 这样我们对 hello_defaults 模块的配置,就会附加到 hello 模块上。

2. 添加 hook

在 hello 目录下添加 hello.go 文件

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
package hello

import (
"android/soong/android"
"android/soong/cc"
"fmt"
)

func init() {
android.RegisterModuleType("cc_hello_binary", helloDefaultsFactory)
}

func helloDefaultsFactory() (android.Module) {
module := cc.DefaultsFactory()
android.AddLoadHook(module, helloHook)
return module
}

func helloHook(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)
}

init 函数会先被执行, 在 init 里面注册了一个新的模块类型 cc_hello_binary, 对应的函数是 helloDefaultsFactory。
需要注意的是其中 cc.DefaultsFactory 要根据模块类型的不同而不同。

  • cc_binary –> cc.DefaultsFactory
  • cc_library_shared –> cc.LibrarySharedFactory()
  • java_library –> java.LibraryFactory()

这个可以在 build/soong/cc/library.go 和 java.go 中查看, 如 library.go

1
2
3
4
5
6
7
8
func init() {
android.RegisterModuleType("cc_library_static", LibraryStaticFactory)
android.RegisterModuleType("cc_library_shared", LibrarySharedFactory)
android.RegisterModuleType("cc_library", LibraryFactory)
android.RegisterModuleType("cc_library_host_static", LibraryHostStaticFactory)
android.RegisterModuleType("cc_library_host_shared", LibraryHostSharedFactory)
android.RegisterModuleType("cc_library_headers", LibraryHeaderFactory)
}

然后我们给 cc_hello_binary 模块类型添加了一个 hook:helloHook,当有 cc_hello_binary 类型的模块定义是, helloHook 就会被触发执行。
我们希望的条件编译就可以在这个 hook 里面进行操作了。我们可以通过 ctx 来获取各种编译信息, 比如 PlatformSdkVersion, DeviceName。
我们可以通过 ctx.AppendProperties 来添加各种配置,可以配置的东西如下:

1
2
3
4
5
6
7
8
9
10
type props struct {
    // 定义Android.bp中的各个字段
    Cflags []string
    Srcs []string
    Include_dirs []string
    Shared_libs []string
    Local_include_dirs []string
    Static_libs []string
    Export_shared_lib_headers []string
}

这个例子中我们只是添加了一个 Cflags 而已, 如果需要的话,我们可以添加源码文件及头文件目录,及依赖等。

3. 使用传进来的宏

1
2
3
4
5
6
7
#include <cstdio>

int main() {
printf("hello qiushao\n");
printf("PLATFORM_SDK_VERSION = %d\n", PLATFORM_SDK_VERSION);
return 0;
}

4. 编译验证

1
2
3
4
pure:/ # hello
hello qiushao
PLATFORM_SDK_VERSION = 29
pure:/ #