4

1. Soong compilation system

Before the release of Android 7.0, Android only used GNU Make to describe and enforce its build rules. The Make build system is widely supported and used in Android system-level compilation, but it has some drawbacks: compilation is slow, error-prone, not scalable, and difficult to test, while the Soong build system provides exactly the flexibility that Android builds require .

The Soong build system was introduced in Android 7.0 (Nougat) to replace Make. It utilizes the Kati GNU Make clone tool and Ninja build system components to speed up Android builds. Soong's compilation flow chart is as follows.

image.png

Under the Soong compilation system, it was originally intended that the input is a .bp file and the output is a .ninja file, but since the .mk file in the system has not been completely eliminated, kati and ckati tools are provided to convert the .mk/Makefile file to .ninja document. .ninja becomes the assembly file of the compilation system, which does not need to be modified manually. It is equivalent to the configuration file to call compilers such as gcc, java, and clang to compile.

The design idea of the Soong compilation system is to eliminate the if/else logic in the .mk file, so that the .bp file is just a simple compilation logic. These complex selection and configuration logic should be placed at a higher level, such as using a better debugged Python to write.

2. Android.bp

The appearance of Android.bp is to replace the Android.mk file. bp is different from mk file, it is pure configuration, there is no flow control such as branch and loop, and it cannot do arithmetic and logic operations. If control logic is required, it can be written in Android.mk or Go language.

2.1 Example

The modules in the Android.bp file start with the module type, followed by a set of format attributes: name: value. At one point, the grammatical structure of Android.bp is similar to the grammatical structure of JSON, and they are written in the form of key-value pairs, such as .

 android_app {
    name: "Provision",
    srcs: ["**/*.java"],
    platform_apis: true,
    product_specific: true,
    certificate: "platform",
}

Every module must have a name attribute, and the corresponding value must be unique across all name files, with the only two exceptions being the Android.bp attribute value in namespaces and prebuilt modules, which may be duplicated .
The srcs attribute specifies the source files used to build the module as a list of strings. You can use the module reference syntax ":<module-name>" to reference the output of other modules that generate source files, such as genrules or filegroups.

 android_app{}

The above code means that this module is used to build an apk. If you want to assign a name to the module, you can use the following method.

 name: "Provision",

If the android_app module type is specified (at the beginning of the code block), the name of the module needs to be set. This setting names the module and the resulting APK will have the same name as the module, but with an .apk suffix. For example, in this case the generated APK will be named Provision.apk

 srcs: ["**/*.java"],

The srcs of the above code is used to specify the source code location of the current module compilation, and * represents a wildcard.

 platform_apis: true,

After setting this flag, it will use the hide api of sdk to compile. The compiled APK uses the system-level API and must be set to this value. Same as LOCAL_PRIVATE_PLATFORM_APIS in Android.mk.

 product_specific: true,

After setting this flag, the generated apk will be installed in the product partition of the Android system, which has the same function as LOCAL_PRODUCT_MODULE in Android.mk.

certificate is used to specify how the APK is signed. If not specified, the testkey signature is used by default. Same as LOCAL_CERTIFICATE. There are four signature methods in Android:

  • testkey: normal apk, this signature is used by default.
  • platform: This apk completes some core functions of the system. After the access test to the folder existing in the system, the UID of the process where the apk compiled in this way is system is.
  • shared: The apk needs to share data with the home/contacts process.
  • media: This apk is a part of the media/download system.

2.2 Common module types

In Android.bp, we will build what we need based on the module type. The following types are commonly used:

  • cc_library_shared: compiled into a dynamic library, similar to BUILD_SHARED_LIBRARY in Android.mk.
  • cc_binary: compiled into an executable file, similar to BUILD_EXECUTABLE in Android.mk.
  • name: The name of the compiled module, similar to LOCAL_MODULE in Android.mk.
  • srcs: Source files, similar to LOCAL_SRC_FILES in Android.mk.
  • local_include_dirs: Specifies the path to find header files, similar to LOCAL_C_INCLUDES in Android.mk.
  • shared_libs: The dynamic library that the compilation depends on, similar to LOCAL_SHARED_LIBRARIES in Android.mk.
  • static_libs: The static library that the compilation depends on, similar to LOCAL_STATIC_LIBRARIES in Android.mk.
  • cflags: Compile Flag, similar to LOCAL_CFLAGS in Android.mk.

android_app

It is used to build the Android application installation package. It is a commonly used module type in Android system application development, and has the same function as BUILD_PACKAGE in Android.mk.

java_library

java_library is used to build and link the source code into the device's .jar file. By default, java_library has only one variable, which generates a .jar package containing .class files compiled according to the device boot classpath. The resulting jar is not suitable for direct installation on the device and will usually be used as a static_libs dependency of another module.

Specifying installable:true will generate a .jar file containing the classes.dex file, suitable for installation on the device. Specifying host_supported: true will generate two variables, one compiled according to the bootclasspath of the device, and the other compiled according to the bootclasspath of the host.

android_library

android_library builds the source code along with the android resource files and links it into the device's ".jar" file. There is a separate variant of android_library that generates a .jar file containing .class files compiled against the device's bootclasspath, and a .package-res.apk file containing android resources compiled with aapt2. The resulting apk file cannot be installed directly on the device, but can be used as a static_libs dependency of the android_app module.

2.3 Setting variables

In bp, a global variable can be set by the = sign.

 src_path = ["**/*.java"]
android_app {
    name: "Provision",
    srcs: src_path,
    platform_apis: true,
    product_specific: true,
    certificate: "platform",
}

3. Basic grammar

3.1 Data Types

The variables and attributes in Android.bp are strongly typed. The variables change dynamically according to the first assignment, and the attributes are statically set by the module type. The supported types are:

  • boolean (true or false)
  • integer (int)
  • string ("string")
  • list of strings(["string1", "string2"])
  • map({key1: "value1", key2: ["value2"]})

3.2 Conditional Statements

Soong does not support conditional statements in Android.bp files. If the conditional statement needs to be processed in the compilation rules, it needs to be processed in Go. Most conditional statements are converted to map properties, where a value in the map is selected and appended to the top-level property.

For example, to support a specific schema file, the following command can be used:

 cc_library {
  ...
  srcs: ["generic.cpp"],
  arch: {
    arm: {
      srcs: ["arm.cpp"],
    },
    x86: {
      srcs: ["x86.cpp"],
    },
  },
}

3.3 Operators

Strings, lists of strings, and maps can be appended using the + operator. Integers can be summed using the + operator. Appending a map produces the union of the keys in both maps and appends the values of all keys present in both maps.

3.4 Example

Android.bp is located in the Android 10 : packages/apps/Car/Notification directory. The reference example is as follows:

 // 构建可执行程序
android_app {
    // 设定可执行的程序的名称,编译后会生成一个 CarNotification.apk
    name: "CarNotification",
    // 指定java源码的位置
    srcs: ["src/**/*.java"],
    // 指定资源文件的位置
    resource_dirs: ["res"],
    // 允许使用系统hide api
    platform_apis: true,
    // 设定apk签名为 platform
    certificate: "platform",
    // 设定apk安装路径为priv-app
    privileged: true,
    // 是否启用代码优化,android_app中默认为true,java_library中默认为false
    optimize: {
        enabled: false,
    },
    // 是否预先生成dex文件,默认为true。该属性会影响应用的首次启动速度以及Android系统的启动速度
    dex_preopt: {
        enabled: false,
    },
    // 引入java静态库
    static_libs: [
        "androidx.cardview_cardview",
        "androidx.recyclerview_recyclerview",
        "androidx.palette_palette",
        "car-assist-client-lib",
        "android.car.userlib",
        "androidx-constraintlayout_constraintlayout"
    ],
    // 引入java库
    libs: ["android.car"],
    product_variables: {
        pdk: {
            enabled: false,
        },
    },
    // 设定依赖模块。如果安装了此模块,则要还需要安装的其他模块的名称
    required: ["privapp_whitelist_com.android.car.notification"]
}
// As Lib
android_library {
    name: "CarNotificationLib",
    srcs: ["src/**/*.java"],
    resource_dirs: ["res"],
    manifest: "AndroidManifest-withoutActivity.xml",
    platform_apis: true,
    optimize: {
        enabled: false,
    },
    dex_preopt: {
        enabled: false,
    },
    static_libs: [
        "androidx.cardview_cardview",
        "androidx.recyclerview_recyclerview",
        "androidx.palette_palette",
        "car-assist-client-lib",
        "android.car.userlib",
        "androidx-constraintlayout_constraintlayout"
    ],
    libs: ["android.car"],
    product_variables: {
        pdk: {
            enabled: false,
        },
    },
}

4. Frequently Asked Questions

4.1 Introducing third-party jars

In actual development, it is often necessary to introduce third-party jars into the app. In Android.bp, the introduction of third-party jars can be done in the following way.

First, create a new libs folder in the root directory of the project, put the jar package to be imported, such as CarServicelib.jar, and then import the jar in Android.bp.

 java_import {
  name: "CarServicelib.jar",
  jars: ["libs/CarServicelib.jar"],
}
android_app {
    // 省去其它属性
    static_libs: [
        "CarServicelib.jar"
    ],
}

4.2 Introducing AndroidX

During the development process, if you want to import the AndroidX class library, you can refer to the following method to import.

 android_app {
    // 省去其它属性
    // 引入AndroidX库下的lib
    static_libs: [
        "androidx.cardview_cardview",
        "androidx.recyclerview_recyclerview",
        "androidx-constraintlayout_constraintlayout"
    ],
}

refer to:
Soong build system
android.bp compiled by Android


xiangzhihong
5.9k 声望15.3k 粉丝

著有《React Native移动开发实战》1,2,3、《Kotlin入门与实战》《Weex跨平台开发实战》、《Flutter跨平台开发与实战》1,2和《Android应用开发实战》