07 Android系统之Android.bp条件编译

基本概念

1、 背景
条件编译为我们提供了一种一套代码兼容多个版本的解决方案,提高代码的复用率。
在Android7.0之前使用的是Makefle编译,makefile语法支持条件编译,配置到Android.mk文件。
在那以后,开始使用Ninja编译框架,只需要我们配置Android.bp文件,但是bp文件就是一个配置文件,不支持条件编译。
但条件编译又是强需求,所以 google 还是提供了一种条件编译的方法,下面我们就来学习一下。

2、 目的
把 platform sdk version 传给一个 Android.bp 模块的 cpp 代码。

3、 文件组织结构

1
2
3
4
5
6
7
hinzer@ubuntu:android-10$ tree ./device/mi/pure/hello
./device/mi/pure/hello  # 我们要编译的hello模块
├── Android.bp      #
├── hello.cpp          #
└── hello.go      #

0 directories, 3 files

关键步骤

1、 模块编译规则(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
31
32
33
// 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

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

查看Android.bp中已知的模块类型,其中

  • 类型为bootstrap_go_package的模块名soong-hello,指定了源文件hello.go
  • 类型cc_hello_binary是在hello.go里面进行定义,这里相当于编译条件。
  • 类型cc_binary的模块名hello,通过hello_defaults模块合入配置,并编译源文件hello.cpp成二进制文件。

2、 添加hook机制(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)
}

hello.go文件中配置hook机制,首先定义模块类型cc_hello_binary,然后是hook处理函数helloHook。如果这个cc_hello_binary被定义,表示编译条件成立,触发hook处理函数

  • init()里面注册了一个新的模块类型 cc_hello_binary
  • 对应的函数是helloDefaultsFactory(),需要注意的是其中cc.DefaultsFactory要根据模块类型的不同而不同,参考build/soong/cc/xxx.go中的定义
  • 最后我们希望的条件编译内容可以在hook中定义,通过ctx来获取和添加配置信息,可添加的配置类型参考类型props的定义

3、 使用传入的配置(hello.cpp)

1
2
3
4
5
6
#include <cstdio>

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

4、 将模块添加到Build系统(product.mk)
将模块添加到product.mk文件中的PRODUCT_PACKAGES变量(模块会随着编译打包到android系统中)。

编译验证

1
2
3
4
5
6
7
8
9
10
11
hinzer@ubuntu:android-10$ mm hello
...
hinzer@ubuntu:android-10$ emulator -wipe-data
...
hinzer@ubuntu:android-10$ adb shell
adb server version (41) doesn't match this client (39); killing...
* daemon started successfully
pure:/ # hello                  # 模块加载
PLATFORM_SDK_VERSION = 29
pure:/ # ^C
130|pure:/ # exit

思考总结

Android.bp只是一个json格式的配置文件,不支持条件语句,不支持控制流程语句。所有复杂问题都由用Go编写的编译逻辑处理。
对于bp文件,了解json格式,其他的靠以后慢慢积累;对于go语言,也不用单独学习,以后多写几个demo,总结套路就好。

参考资料

  • 官方文档 - Soong编译系统
  • Android.bp 条件编译