天天看点

Android.bp 语法浅析-Android10.0编译系统(八)

摘要:Blueprint解析Android.bp到ninja的代码流程时如何走的?

阅读本文大约需要花费18分钟。

文章首发微信公众号:IngresGe

专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢!

欢迎关注我的公众号!

Android.bp 语法浅析-Android10.0编译系统(八)

[Android取经之路] 的源码都基于Android-Q(10.0) 进行分析

[Android取经之路] 系列文章:

《系统启动篇》

  1. Android系统架构
  2. Android是怎么启动的
  3. Android 10.0系统启动之init进程
  4. Android10.0系统启动之Zygote进程
  5. Android 10.0 系统启动之SystemServer进程
  6. Android 10.0 系统服务之ActivityMnagerService
  7. Android10.0系统启动之Launcher(桌面)启动流程
  8. Android10.0应用进程创建过程以及Zygote的fork流程
  9. Android 10.0 PackageManagerService(一)工作原理及启动流程
  10. Android 10.0 PackageManagerService(二)权限扫描
  11. Android 10.0 PackageManagerService(三)APK扫描
  12. Android 10.0 PackageManagerService(四)APK安装流程

《日志系统篇》

  1. Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性
  2. Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化
  3. Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析
  4. Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现​

《Binder通信原理》:

  1. Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
  2. Android10.0 Binder通信原理(二)-Binder入门篇
  3. Android10.0 Binder通信原理(三)-ServiceManager篇
  4. Android10.0 Binder通信原理(四)-Native-C\C++实例分析
  5. Android10.0 Binder通信原理(五)-Binder驱动分析
  6. Android10.0 Binder通信原理(六)-Binder数据如何完成定向打击
  7. Android10.0 Binder通信原理(七)-Framework binder示例
  8. Android10.0 Binder通信原理(八)-Framework层分析
  9. Android10.0 Binder通信原理(九)-AIDL Binder示例
  10. Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub设计模式
  11. Android10.0 Binder通信原理(十一)-Binder总结

  《HwBinder通信原理》

  1. HwBinder入门篇-Android10.0 HwBinder通信原理(一)
  2.  HIDL详解-Android10.0 HwBinder通信原理(二)
  3. HIDL示例-C++服务创建Client验证-Android10.0 HwBinder通信原理(三)
  4. HIDL示例-JAVA服务创建-Client验证-Android10.0 HwBinder通信原理(四)
  5. HwServiceManager篇-Android10.0 HwBinder通信原理(五)
  6. Native层HIDL服务的注册原理-Android10.0 HwBinder通信原理(六)
  7. Native层HIDL服务的获取原理-Android10.0 HwBinder通信原理(七)
  8. JAVA层HIDL服务的注册原理-Android10.0 HwBinder通信原理(八)
  9. JAVA层HIDL服务的获取原理-Android10.0 HwBinder通信原理(九)
  10. HwBinder驱动篇-Android10.0 HwBinder通信原理(十)
  11. HwBinder原理总结-Android10.0 HwBinder通信原理(十一)

《编译原理》

  1. 编译系统入门篇-Android10.0编译系统(一)
  2. 编译环境初始化-Android10.0编译系统(二)
  3. make编译过程-Android10.0编译系统(三)
  4. Image打包流程-Android10.0编译系统(四)
  5. Kati详解-Android10.0编译系统(五)
  6. Blueprint简介-Android10.0编译系统(六)
  7. Blueprint代码详细分析-Android10.0编译系统(七)

1 概述

  在 Android 7.0 之前,Android 编译系统使用 GNU Make 描述和shell来构建编译规则,模块定义都使用Android.mk进行定义,Android.mk的本质就是Makefile,但是随着Android的工程越来越大,模块越来越多,Makefile组织的项目编译时间越来越长。这样下去Google工程师觉得不行,得要优化。

  因此,在Android7.0开始,Google采用ninja来代取代之前使用的make,由于之前的Android.mk数据实在巨大,因此Google加入了一个kati工具,用于将Android.mk转换成ninja的构建规则文件buildxxx.ninja,再使用ninja来进行构建工作。

ninja的网址:https://ninja-build.org

编译速度快了一些,但是既然要干, 那就干个大的,最终目标要把make都取代,于是从Android8.0开始,Google为了进一步淘汰Makefile,因此引入了Android.bp文件来替换之前的Android.mk。

  Android系统的编译历程:

Android.bp 语法浅析-Android10.0编译系统(八)

2 Android.bp 文件格式

  根据设计,Android.bp 文件很简单。它们不包含任何条件语句,也不包含控制流语句;所有复杂问题都由用 Go 编写的构建逻辑处理。Android.bp 文件的语法和语义都尽可能与 Bazel BUILD 文件类似。

2.1 模块

  Android.bp 文件中的模块以模块类型开头,后跟一组 name: "value", 格式的属性:

cc_binary {
    name: "gzip",
    srcs: ["src/test/minigzip.c"],
    shared_libs: ["libz"],
    stl: "none",
}
           

 每个模块都必须具有 name 属性,并且相应值在所有 name 文件中必须是唯一的,仅有两个例外情况是命名空间和预构建模块中的 Android.bp 属性值,这两个值可能会重复。

  srcs 属性以字符串列表的形式指定用于构建模块的源文件。您可以使用模块引用语法 ":<module-name>" 来引用生成源文件的其他模块的输出,如 genrule 或 filegroup。

如需有效模块类型及其属性的列表,请参阅 Soong 模块参考:

  https://www.cnblogs.com/linhaostudy/p/12361659.html

常用模块类型:

  • cc_binary
  • cc_library
  • cc_library_static
  • android_app
  • java_library
  • hidl_interface
  • aidl_interface

2.2 类型

  变量和属性是强类型,变量根据第一项赋值动态变化,属性由模块类型静态设置。支持的类型为:

  • 布尔值Bool(true 或 false)
  • 整数Integers (int)
  • 字符串Strings ("string")
  • 字符串列表List of string (["string1", "string2"])
  • 映射Maps ({key1: "value1", key2: ["value2"]})

  映射可以包含任何类型的值,包括嵌套映射。列表和映射可能在最后一个值后面有终止逗号。

2.3 Glob

  接受文件列表的属性(例如 srcs)也可以采用 glob 模式。glob 模式可以包含普通的 UNIX 通配符 *,例如 *.java。glob 模式还可以包含单个 ** 通配符作为路径元素,与零个或多个路径元素匹配。例如,java*.java 同时匹配 java/Main.java 和 java/com/android/Main.java 模式。

2.4 变量

  Android.bp 文件可能包含顶级变量赋值:

gzip_srcs = ["src/test/minigzip.c"]
cc_binary {
    srcs: gzip_srcs,
    shared_libs: ["libz"],
    stl: "none",
}
           

  变量的作用域限定在声明它们的文件的其余部分,以及所有子 Android.bp文件,可以使用 “=” 号赋值, 但是不能使用 “:=” 赋值。变量是不可变的,但有一个例外情况:可以使用 += 赋值将变量附加到别处,但只能在引用它们之前附加。

 例:

gzip_srcs = ["src/test/minigzip.c"]
gzip_srcs += [
    "src/test/test.cpp",
]
cc_binary {
    srcs: gzip_srcs,
    shared_libs: ["libz"],
    stl: "none",
}
           

2.5 注释

  Android.bp 文件可以包含 C 样式的多行 注释以及 C++ 样式的单行 // 注释。

     例如:

// This is a "//" Comments test
// =========================================================

/*This is a "/**/" Comments test*/
           

2.6 运算符

  可以使用 + 运算符附加字符串、字符串列表和映射。可以使用 + 运算符对整数求和。附加映射会生成两个映射中键的并集,并附加在两个映射中都存在的所有键的值。

2.7 条件语句

  Soong 不支持 Android.bp 文件中的条件语句。但是,编译规则中需要条件语句的复杂问题将在 Go(在这种语言中,您可以使用高级语言功能,并且可以跟踪条件语句引入的隐式依赖项)中处理。大多数条件语句都会转换为映射属性,其中选择了映射中的某个值并将其附加到顶级属性。

  例如,要支持特定于架构的文件,请使用以下命令:

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

2.8 格式设置工具

  Soong 包含一个针对 Blueprint 文件的规范格式设置工具,类似于 gofmt。如需以递归方式重新设置当前目录中所有 Android.bp 文件的格式,请运行以下命令:

bpfmt -w .
           

  规范格式包括缩进四个空格、多元素列表的每个元素后面有换行符,以及列表和映射末尾有英文逗号。

2.9 默认模块

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

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

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

2.10 预编译的模块

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

2.11 命名空间模块

  在 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 模块都会被视为处于 Android.bp(位于当前目录或最近的父级目录中的 soong_namespace 文件内)定义的命名空间中。如果未找到此类 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 在根命名空间中查找。

2.12 Android.mk文件 自动转Android.bp方法

  Android源码里边提供了快捷直接Android.mk转换成Android.bp的工具:androidmk。

  androidmk源码位置:

build/soong/androidmk/cmd/androidmk/androidmk.go
           

编译出来后androidmk可执行文件位置:

aosp/out/soong/host/linux-x86/bin/androidmk
           

转换方法:

aosp/out/soong/host/linux-x86/bin/androidmk [Android.mk PATH] > [Android.bp PATH]
           

  该工具可以转换变量、模块、注释和一些条件,但任何自定义生成文件规则、复杂条件或额外的包含都必须手动转换。

参考:

《SOONG构建系统》

《Soong Modules Reference》

继续阅读