Posts Android.bp syntax and usecase
Post
Cancel

Android.bp syntax and usecase

在实际开发过程中,经常会遇到通过 宏/环境变量 判断是否加载某个模块,这时就需要进行条件编译。

Android 7.0 前使用 Android.mk 来编译模块,makefile 支持条件编译;但是 Android 7.0 之后逐步被 Android.bp 取代,Android.bp 本质上是 json 配置文件,不支持条件判断,所以 Google 提供了通过 go 语言来实现 Android.bp 的条件编译。

相关的的官方描述:Soong 编译系统

Android.bp语法

模块

ModuleDesc
cc_defaults父模块,可以通过 defaults 属性被其它模块继承
cc_library编译成库文件
cc_binary编译成可执行文件

属性

AttributeDesc
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.gocc.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 来获取各种编译信息, 比如 PlatformSdkVersionDeviceName

我们可以通过 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:

Soong 编译系统

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

This post is licensed under CC BY 4.0 by the author.