Android.bp 语法浅析

本文介绍了Android构建系统的演变,从传统的Makefile和Android.mk到Android.bp的过渡。Android 7.0引入了Ninja提高构建速度,并使用kati将Android.mk转换为Ninja文件。从Android 8.0开始,Android.bp成为主要配置文件,简化了构建过程。Android.bp定义了模块类型,如cc_library_shared,包含了编译、依赖库等信息。同时,文章提供了一个Android.bp的实际示例,展示了如何配置应用程序模块。转换工具androidmk可以将Android.mk转为Android.bp,但复杂的规则需手动调整。

随着 android 工程越来越大,包含的 module 越来越多,以 makefile 组织的项目编译花费的时间越来越多。Google 在 7.0 引入了 ninja 是 Google 的一名程序员推出的注重速度的构建工具,一般在 Unix/Linux 上的程序通过 make/makefile 来构建编译,而 Ninja 通过将编译任务并行组织,大大提高了构建速度。

7.0 的时候 android 项目还是有大部分是由 makefile 来组织的,因此 Google 引入了 kati 将 makefile 翻译成 ninja 文件。8.0 开始,android 引入了 Android.bp 来替代 之前的 Android.mk 文件,不同于Android.mk,Android.bp只是纯粹的配置文件,不包括分支、循环等流程控制。在 android 项目上如何进行选择编译、解析配置、转换成 ninja 等,Soong 就被创造出来,将 Android.bp 转换为ninja文件进行管理。

简而言之,kati 把 bp 直接翻译成 ninja,blueprint / soong 则把 bp 翻译成 ninja。

Android.bp --> Blueprint --> Soong --> Ninja
Makefile or Android.mk --> kati --> Ninja
  •  

1. 模块

cc_library_shared {				//编译成动态库,类似于Android.mk中的BUILD_SHARED_LIBRARY
    name: "libbluetooth_jni",	//编译出的模块的名称,类似于Android.mk中的LOCAL_MODULE
    srcs: [ 					//源文件,类似于Android.mk中的LOCAL_SRC_FILES
        "com_android_bluetooth_btservice_AdapterService.cpp"
    ],
    include_dirs: [				//用户指定的头文件查找路径,类似于Android.mk中的LOCAL_C_INCLUDES
        "libnativehelper/include/nativehelper",
        "system/bt/types",
    ],
    shared_libs: [				//编译所依赖的动态库,类似于Android.mk中的LOCAL_SHARED_LIBRARIES
        "libandroid_runtime",
        "libchrome",
    ],
    static_libs: [				//编译所依赖的静态库,类似于Android.mk中的LOCAL_STATIC_LIBRARIES
        "libbluetooth-types",
    ],
    cflags: [					///编译flag,类似于Android.mk中的LOCAL_CFLAGS
        "-Wall",
        "-Wextra",
        "-Wno-unused-parameter",
    ],
}
subdirs = ["subdir1", "subdir2"]
  •  

定义一个模块从模块的类型开始,模块有不同的类型,上面例子中的 cc_library_shared 声明编译成动态库,当然类型还有很多种,譬如 cc_binary android_app、cc_library_static 等等。模块包含一些属性格式为 property-name:property-value,其中name属性必须指定,其属性值必须是全局唯一的。

动态库:可以被 install/copy 到应用程序包(apk)
静态库:可以被链接入动态库

  • name : 模块的名称
  • src : 模块的源码
  • include_dirs : 指定的头文件查找路径
  • shared_libs : 编译时依赖的动态库
  • static_libs : 编译时依赖的静态库
  • subdirs : 是一个文件级的顶层属性,指定后会查找次级目录下的Android.bp。

2. 示例

我们来看一下,android 中一个实际的 bp 文件:

android_app {
    name: "NFC",
    srcs: [
        "src/**/*.java",
        "nci/**/*.java",
        ":statslog-Nfc-java-gen",
    ],
    platform_apis: true,
    certificate: "platform",
    jni_libs: ["libsn100nfc_nci_jni"],
    libs: ["com.nxp.nfc.nq"],
    static_libs: [
        "androidx.appcompat_appcompat",
        "nearme_nfc",
        ],
    optimize: {
        enabled: true,
        obfuscate: true,
        proguard_flags_files: ["proguard.flags"],
    },
}

java_import {
    name: "nearme_nfc",
    host_supported: true,
    jars: [
        "libs/env.jar",
    ],
}
  •  
  • platform_apis : 用 sdk 的 hide 的 api 來编译
  • certificate : 指定用的是什么签名,如上用的是 platform 签名。
  • jni_libs : 依赖使用的 JNI 库
  • libs : 工程中的 libs 库
  • static_libs : 静态库,其中 nearme_nfc 为下方定义的:java_import
  • optimize : 压缩配置,enabled 是否开启,obfuscate 是否开启混淆,proguard_flags_files 混淆规则配置文件
  • host_supported : Android.bp 文件要求每个模块拥有唯一的名称,但每个模块可以内置多种变化,例如可以添加 host_supported: true。

3. 转换 mk

Soong 包含了一个工具,可以将 Android.mk 文件转换为 Android.bp 文件:
androidmk Android.mk > Android.bp

工具位于:android\out\soong\host\linux-x86\bin\androidmk 该工具可以转换变量,模块,注释和一些条件,但任何自定义的 Makefile 规则,复杂条件或额外的 include 必须手动转换。

<think>我们正在讨论的是Android.bp文件,根据引用[3]:"Android.bp文件很简单。它们不包含任何条件语句,也不包含控制流语句;所有复杂问题都由用 Go编写的构建逻辑处理。"因此,在Android.bp中不能直接使用条件语句(如if-else)来动态设置enabled属性。但是,我们可以通过以下两种方式实现条件编译:1. **使用`soong_config_bool_var`和`soong_config_modules`**:这是Soong构建系统提供的一种机制,允许在构建时根据产品变量(如TARGET_PRODUCT)或编译类型(如eng,user)来条件地启用或禁用模块。2.**使用`override`机制**:在特定产品的Makefile(例如device/<vendor>/<product>/product.mk)中覆盖模块的enabled属性。下面分别介绍这两种方法: ###方法1:使用soong_config_bool_var(推荐)步骤:a.在`build/soong/android/variable.go`中定义一个新的布尔变量(例如`my_product_enabled`),并关联到产品变量。b.Android.bp中使用`soong_config_module_type`或直接在模块中使用条件选择。但是,由于修改`variable.go`是平台级别的修改,对于设备厂商来说,通常可以在`device/<vendor>/<product>/`目录下通过创建`BoardConfig.mk`和`soong_config.mk`来定义变量,然后通过`Android.bp`读取这些变量。具体步骤(以设备厂商的角度):1.**在设备目录下创建`soong_config.mk`**(例如:`device/vendor/product/soong_config.mk`):```makefileSOONG_CONFIG_NAMESPACES +=my_namespaceSOONG_CONFIG_my_namespace+= my_product_enabledSOONG_CONFIG_my_namespace_my_product_enabled ?= false```2.**在`BoardConfig.mk`中设置变量**(例如,根据产品类型设置):```makefile#假设产品类型为"my_product"ifeq ($(TARGET_PRODUCT),my_product)SOONG_CONFIG_my_namespace_my_product_enabled:= trueendif```3. **在Android.bp中引用该变量**:```pythonsoong_config_bool_variable {name: "my_product_enabled",}cc_binary {name: "my_module",//使用soong_config_module_type定义的条件模块类型//或者直接在enabled属性中使用条件enabled:soong_config_get(my_namespace, my_product_enabled),srcs:["src/*.cpp"],}```然而,实际上在Android.bp中,我们可以通过`soong_config_get`函数来获取变量的值,并直接用于enabled属性。注意:`soong_config_get`函数返回的是布尔值,因此可以直接赋值给enabled。但是,需要注意的是,`soong_config_bool_variable`模块类型是在Android10(Q)之后引入的,请确保你的平台版本支持。###方法2:使用override机制(较传统)在产品的Makefile中(例如`device.mk`或`product.mk`)中,我们可以使用`PRODUCT_PACKAGES`来添加或移除模块,但对于非安装模块(如cc_binary)可能不适用。另外,对于enabled属性,我们可以通过`override`指令在产品的Android.bp文件中覆盖。步骤:a.在公共的Android.bp中定义模块,并设置默认的enabled为false。b.在特定产品的目录下(例如`device/vendor/product/`)创建Android.bp文件,然后覆盖该模块的enabled属性。示例:公共Android.bp(例如:`common/Android.bp`):```pythoncc_binary {name: "my_module",enabled:false,//默认不启用srcs:["src/*.cpp"],} ```特定产品的Android.bp(例如:`device/vendor/product/Android.bp`): ```python//覆盖公共模块的enabled属性override_cc_binary {name: "my_module",enabled:true,} ```但是,这种方法需要为每个产品创建单独的Android.bp文件,并且只能覆盖已经存在的模块。 ###总结根据需求,如果条件是基于产品类型(TARGET_PRODUCT),那么方法1(soong_config)是更灵活和推荐的方式。方法2则适用于产品目录结构清晰且需要覆盖模块属性的情况。由于Android.bp本身不支持条件语句,所以我们必须依赖Soong构建系统提供的外部条件变量机制(soong_config)或者通过覆盖机制来实现。请根据你的Android平台版本和具体需求选择合适的方法。注意:在Android.bp中,enabled属性是布尔类型,所以必须使用布尔值(true或false)进行赋值,不能使用字符串。引用说明: [^3]:Android.bp文件设计简单,不包含条件语句,复杂逻辑由Go编写的构建逻辑处理。[^4]:提供了Android.mk中的宏和Android.bp的对应关系,但这里我们讨论的是条件编译。相关问题:1.如何为多个产品类型配置不同的模块启用状态?2.在Soong构建系统中,除了soong_config,还有哪些条件编译的方法?3.如何调试Android.bp中模块的enabled属性?4.如何为自定义产品添加新的构建变量?5.Android.bp中如何根据编译类型(user,eng)启用模块?</think>在 Android 构建系统中,根据产品类型动态启用模块的正确实现方式如下: ### 正确实现方案 根据 Android 构建系统规范[^3],`Android.bp` 文件本身**不支持条件语句**,但可以通过以下两种方式实现动态启用: #### 1. 使用 `soong_config_module_type` (推荐) ```python // 声明配置变量 soong_config_bool_variable { name: "my_product_enabled", } // 定义模块 cc_binary { name: "my_module", srcs: ["src/*.cpp"], enabled: soong_config_get(my_namespace, my_product_enabled), } ``` 在 `BoardConfig.mk` 中设置变量值: ```makefile # 设置产品特定变量 SOONG_CONFIG_NAMESPACES += my_namespace SOONG_CONFIG_my_namespace += my_product_enabled SOONG_CONFIG_my_namespace_my_product_enabled = true ``` #### 2. 使用 `override` 机制 ```python // 基础模块定义 (默认禁用) cc_binary { name: "my_module_base", enabled: false, srcs: ["src/*.cpp"], } // 产品特定覆盖 override_cc_binary { name: "my_module_base", enabled: true, } ``` ### 关键原则 1. **布尔值直接赋值** `enabled` 属性必须使用布尔值 `true`/`false`,不能加引号[^1]: ```python enabled: true // ✅ 正确 enabled: "true" // ❌ 错误 ``` 2. **避免条件语句** 遵循 `Android.bp` 设计原则[^3]: ```python // ❌ 禁止使用条件语句 if (target == "android") { enabled: true } ``` 3. **模块类型一致性** 所有模块必须有明确的类型声明(如 `cc_binary`)和全局唯一的 `name` 属性[^2]: ```python cc_binary { // 模块类型 name: "unique_module_name", // 全局唯一标识 ... } ``` ### 完整示例 ```python // 声明产品配置变量 soong_config_bool_variable { name: "enable_my_product_feature", } // 主模块定义 cc_binary { name: "my_dynamic_module", srcs: ["src/*.cpp"], enabled: soong_config_get(my_product, enable_my_product_feature), cflags: [ "-DMODULE_ENABLED", // 条件编译宏 ], } ``` 在 `BoardConfig.mk` 中激活产品特性: ```makefile SOONG_CONFIG_NAMESPACES += my_product SOONG_CONFIG_my_product += enable_my_product_feature SOONG_CONFIG_my_product_enable_my_product_feature = true ``` ### 调试建议 1. 检查变量传递: ```bash m soong_dump # 查看所有配置变量 ``` 2. 验证模块状态: ```bash m --dumpvars-mode get_make_var my_dynamic_module ``` ### 相关问题 1. 如何在 Soong 构建系统中定义自定义配置变量? 2. `override_*` 机制支持哪些模块属性的重写? 3. 如何为不同的产品类型设置不同的编译宏? 4.Android 构建系统中如何调试条件编译问题? 5. `soong_config_get` 函数支持哪些数据类型? [^1]: Android.bp 注释,Android.bp入门指南之浅析Android.bp语法 [^2]: 普法Android.mk中的一些宏和Android.bp对应关系 [^3]: 根据设计,Android.bp 文件很简单。它们不包含任何条件语句,也不包含控制流语句;所有复杂问题都由用 Go 编写的构建逻辑处理
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值