从 Make 转换为 Soong

在 Android 7.0 发布之前,Android 仅使用 GNU Make 描述和执行其 build 规则。Make 构建系统得到了广泛的支持和使用,但在 Android 层面变得缓慢、容易出错、无法扩展且难以测试。Soong 构建系统正好提供了 Android build 所需的灵活性。

因此,平台开发者应尽快从 Make 切换到 Soong。如果遇到问题,请发送至 android-building Google 群组寻求支持。

什么是 Soong?

Soong 构建系统是在 Android 7.0 (Nougat) 中引入的,旨在取代 Make。它利用 Kati GNU Make 克隆工具和 Ninja 构建系统组件来加速 Android 的构建。

如需了解一般说明,请参阅 Android 开源项目 (AOSP) 中的 Android Make 构建系统说明;如需了解从 Make 转换到 Soong 所需执行的修改,请参阅 Android.mk 编写人员须知的构建系统变更

如需查看关键术语的定义,请参阅词汇表中与 build 相关的条目;如需了解完整详情,请参阅 Soong 参考文件

Make 和 Soong 比较

以下是 Make 配置与 Soong 在 Soong 配置(Blueprint 或 .bp)文件中完成相同操作的比较。

Make 示例

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := libxmlrpc++
LOCAL_MODULE_HOST_OS := linux

LOCAL_RTTI_FLAG := -frtti
LOCAL_CPPFLAGS := -Wall -Werror -fexceptions
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/src

LOCAL_SRC_FILES := $(call \
     all-cpp-files-under,src)
include $(BUILD_SHARED_LIBRARY)

Soong 示例

cc_library_shared {
     name: "libxmlrpc++",

     rtti: true,
     cppflags: [
           "-Wall",
           "-Werror",
           "-fexceptions",
     ],
     export_include_dirs: ["src"],
     srcs: ["src/**/*.cpp"],

     target: {
           darwin: {
                enabled: false,
           },
     },
}

如需查看测试专用 Soong 配置示例,请参阅简单的 build 配置

如需查看 Android.bp 文件中各个字段的说明,请参阅 Android.bp 文件格式

特殊模块

一些特殊模块组具有独特的特征。

默认模块

默认模块可用于在多个模块中重复使用相同的属性。例如:

cc_defaults {
    name: "gzip_defaults",
    shared_libs: ["libz"],
    stl: "none",
}

cc_binary {
    name: "gzip",
    defaults: ["gzip_defaults"],
    srcs: ["src/test/minigzip.c"],
}

预编译的模块

某些预构建的模块类型允许模块与其基于源代码的对应模块具有相同的名称。例如,如果已有同名的 cc_binary,也可以将 cc_prebuilt_binary 命名为 foo。这让开发者可以灵活地选择要纳入其最终产品中的版本。如果 build 配置包含两个版本,那么预编译模块定义中的 prefer 标志值会指示哪个版本具有优先级。请注意,某些预编译模块的名称不能以 prebuilt开头,例如 android_app_import

命名空间模块

在 Android 完全从 Make 转换为 Soong 之前,Make 产品配置必须指定 PRODUCT_SOONG_NAMESPACES 值。它的值应该是一个以空格分隔的列表,其中包含 Soong 导出到 Make 以使用 m 命令进行编译的命名空间。在 Android 完成到 Soong 的转换之后,启用命名空间的详细信息可能会发生变化。

Soong 可以让不同目录中的模块指定相同的名称,只要每个模块都在单独的命名空间中声明即可。可以按如下方式声明命名空间:

soong_namespace {
    imports: ["path/to/otherNamespace1", "path/to/otherNamespace2"],
}

请注意,命名空间没有 name 属性;其路径会自动指定为其名称。

系统会根据每个 Soong 模块在树中的位置为其分配命名空间。每个 Soong 模块都会被视为处于 soong_namespace(位于当前目录或最近的父级目录中的 Android.bp 文件内)定义的命名空间中。如果未找到此类 soong_namespace 模块,则认为该模块位于隐式根命名空间中。

下面是一个示例:Soong 尝试解析由模块 M 在名称空间 N(导入命名空间 I1、I2、I3…)中声明的依赖项 D。

  1. 如果 D 是 //namespace:module 格式的完全限定名称,系统将仅在指定的命名空间中搜索指定的模块名称。
  2. 否则,Soong 将首先查找在命名空间 N 中声明的名为 D 的模块。
  3. 如果该模块不存在,Soong 会在命名空间 I1、I2、I3…中查找名为 D 的模块。
  4. 最后,Soong 在根命名空间中查找。