1. 编译系统变化

Android 最初是用 Android.mk 来定义模块的, Android.mk 本质上就是 Makefile。随着 Android 工程越来越大,包含的模块越来越多,以 Makefile 组织的项目编译花费的时间越来越多。google 在 Android 7.0 开始引入了 ninja 编译系统。相对于 make 来说 ninja 在大的项目管理中速度和并行方面有突出的优势,因此 google 采用了 ninja 来取代之前使用的 make。关于 ninja 的介绍,请参考 ninja 构建系统 和 ninja 官方说明文档。由于 Android.mk 的数量巨大且复杂,不可能把所有的 Android.mk 改写成 ninja 的构建规则,因此 google 搞了个 kati 工具,用于将 Androd.mk 转换成 ninja 的构建规则文件 build.ninja,再使用 ninja 来进行构建工作。

Android 8.0 开始,google 引入了 Android.bp 文件来替代之前的 Android.mk 文件,Android.bp 只是纯粹的配置文件,不包括分支、循环等流程控制, 本质上就是一个 json 配置文件。同时还引入 Soong 这个工具,用于将 Android.bp 转换为 ninja 的构建规则文件 build.ninja,再使用 ninja 来进行构建工作。但之前的模块全部是用 Android.mk 来定义的,google 不可能一下子把所有模块都修改成 Android.bp,只能逐步替换,目前 Android 10 上还有少部分 Android.mk 的模块,按这进度,估计要到 Android 12 才能完全替换成 Android.bp 了。无论是 Android.mk 还是 Android.bp 最后都是转化成 ninja 的构建规则,再进行编译的。 各种文件跟工具的关系如下:

2. 编译参数配置

Android编译系统的配置,可以分为三个层级,从下到上依次是:

2.1 平台级(Board)参数配置

平台级配置主要在 BoardConfig.mk 文件中参数主要有以下几个类别的配置:

  • CPU 体系结构: TARGET_ARCH, TARGET_CPU_ABI, TARGET_ARCH_VARIANT等。配置 CPU 是 x86 架构还是 arm 架构, arm 架构下又分好几个版本。编译系统会根据这个配置去加载对应 CPU 构架的 mk:build/core/combo/arch/$(TARGET_ARCH)/$(TARGET_ARCH_VARIANT).mk
  • 内核参数配置: BOARD_KERNEL_BASE, BOARD_KERNEL_PAGESIZE, BOARD_KERNEL_CMDLINE 等, 这些参数最终会被打包到 boot 分区的镜像文件中(boot.img),作为内核的启动参数。
  • 分区镜像: TARGET_USERIMAGES_USE_EXT4, BOARD_BOOTIMAGE_PARTITION_SIZE, BOARD_SYSTEMIMAGE_PARTITION_SIZE 等与分区格式、分区大小相关的参数。system, vendor 等分区的大小就是在这里面配置的。
  • 外设参数配置: 蓝牙, wifi 等外接设备的参数配置。

2.2 产品级(Product)的参数配置

同一块板子可以做不同的产品,各产品之间的差异主要是软件方面的差异,比如内置的应用不一样, 默认语言不一样等。AndroidProducts.mk 文件中的 PRODUCT_MAKEFILES 列出了所有产品的配置文件。
每一个产品都会有一个对应的 $(PRODUCT_NAME).mk 文件, 这个产品的所有配置都在里面列出,主要的配置项目如下:

  • PRODUCT_NAME : 产品名, 我们通过 lunch 选择的产品, 就要与这个配置保持一致, 同时产品的 mk 文件也要命名为 $(PRODUCT_NAME).mk。
  • PRODUCT_DEVICE : 板子的名称,编译系统会使用该名称查找 BoardConfig.mk。产品的输出目录也是根据该名称创建 out/target/product/$PRODUCT_DEVICE。
  • PRODUCT_BRAND : 对软件进行自定义所针对的品牌(如果有),例如运营商。
  • PRODUCT_MANUFACTURER : 制造商的名称, 会赋值给 ro.product.manufacturer 属性。
  • PRODUCT_MODEL : 最终产品的最终用户可见名称, 或者叫机型名。
  • PRODUCT_LOCALES : 以空格分隔的列表,用于列出由双字母语言代码和双字母国家/地区代码组成的代码对,以便说明针对用户的一些设置,例如界面语言和时间、日期以及货币格式。PRODUCT_LOCALES 中列出的第一个语言区域会被用作产品的默认语言区域。
  • PRODUCT_PACKAGES : 列出要安装的 APK 和模块。
  • PRODUCT_COPY_FILES : 预置文件列表,例如 source_path:destination_path。在编译相应产品时,应将源路径下的文件复制到目标路径。
  • PRODUCT_PROPERTY_OVERRIDES : 系统属性列表(采用“key=value”格式)列表。
  • PRODUCT_PACKAGE_OVERLAYS : 资源 overlay 目录。

以上是一个产品的常见配置, 基本上每一个产品都会用到这些配置, 作为一个系统 rom 开发者, 我们要非常熟悉这些配置的作用。
对于产品的配置还有一个重要的配置:编译类型(TARGET_BUILD_TYPE), 这个配置是我们在 lunch 选择要编译的产品的时候选择的。
我们在添加产品的时候有以下配置,把产品添加到 lunch 的选择列表中,其中的形式就是 $(PRODUCT_NAME)-$(TARGET_BUILD_TYPE)

1
2
3
4
COMMON_LUNCH_CHOICES := \pure-eng \pure-userdebug \pure-user

编译类型的取值范围为:eng, user, userdebug。这三者的区别如下:

    1. eng 工程师版本, 主要用于开发调试阶段:

      • 安装带有 eng 或 debug 标记的模块。
      • 除了带有标记的模块之外,还会根据产品定义文件安装相应模块。
      • ro.secure=0
      • ro.debuggable=1
      • ro.kernel.android.checkjni=1
      • adb 默认处于启用状态。
      • WITH_DEXPREOPT 可以设置为 false, 即在编译时不对系统映像上安装的 DEX 代码调用 dex2oat。 具体作用下面单独讲。
    1. user 用户发布版本:

      • 安装带有 user 标记的模块。
      • 除了带有标记的模块之外,还会根据产品定义文件安装相应模块。
      • ro.secure=1
      • ro.debuggable=0
      • adb 默认处于停用状态。
      • WITH_DEXPREOPT 只能为 true, 不能设置为 false。
    1. userdebug 用户调试版本, 除了以下几点之外,其余均与 user 相同:

      • 还会安装带有 debug 标记的模块。
      • ro.debuggable=1
      • adb 默认处于启用状态。

WITH_DEXPREOPT 意思为预优化, 也就是把 Android 在启动或 APP 在运行时所需要做的一些事情,把这些事情转移到编译 APK 时完成,来达到更快的 Android 系统启动速度和更快的APP运行速度。Android 在首次启动和首次安装应用时,需要将字节码翻译成机器码,这样 Android 系统的启动速度将会大大减慢,如果没有预优化,APP 的运行速度也会加上翻译所需要的时间。所以,这个翻译的工作需要转移到编译上面来,也就是所,在编译 APK 文件时,将会预先对 APK 进行翻译的优化,然后再打包到系统里面去,这样 Android 系统在首次启动时,就不再需要花费大量的时间去翻译 APK 的字节码。

WITH_DEXPREOPT 虽然能够提高系统的首次开机速度和APP的首次运行速度, 但是会大大增加镜像的大小,从经验上来看,会增加1.5倍左右。而且 jar 经过预优化之后, 就没法简单的直接替换生效了。我们在开发过程经常需要修改 framework.jar, services.jar 等, 如果系统不是 eng 版本,替换之后是不生效的, 需要重新编译升级。所以为了提高开发验证速度, 我们需要替换 jar 包的话,就要使用 eng 版本的系统。

2.3 模块级(Module)参数配置

目前模块配置有两种形式,Android.mk 和 Android.bp。这两种模块定义系统是独立的,也就是 Android.mk 中定义的模块,不能被 Android.bp 中的模块依赖。 我们分别讨论一下这两种模块配置形式。

2.3.1 Android.mk

一个模块最基本的四个要素为:模块名, 源文件列表, 依赖关系, 模块类型。对于 Android.mk 模块来说,这几个基本要素的配置方法如下:

1
2
3
4
5
6
7
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := hello          # 模块名
LOCAL_SRC_FILES := hello.cpp    # 源文件列表
LOCAL_SHARED_LIBRARIES := liblog  # 依赖关系
LOCAL_xxx       := xxx
include $(BUILD_EXECUTABLE)       # 模块类型

一个模块通常以以下两行配置作为开头:

1
2
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

这两行配置的作用是:

  • 设置当前模块的编译路径为当前文件夹路径。
  • 清理(可能由其他模块设置过的)编译环境中用到的变量。具体来说是重置除了 LOCAL_PATH 之外的所有以 LOCAL_ 开头的编译环境变量。

Android 源码是一个非常庞大的系统,包含了成千上万个模块,各模块之间的依赖关系也很复杂。为了方便模块的编译, Android 开发团队最初在 Makefile 的基础上,开发了一套编译系统,在这套编译系统下,我们要定义一个模块变得非常简单,只需要定义一系列的编译环境变量就行了,根本不用关心具体的编译细节。下面举例解析一下部分重要的编译环境变量:

  • LOCAL_MODULE:当前模块的名称,这个名称在系统中应当是唯一的,模块间的依赖关系就是通过这个名称来引用的。编译系统会自动添加适当的后缀。例如,libfoo,要产生动态库,则生成libfoo.so。
  • LOCAL_SRC_FILES:当前模块包含的所有源代码文件。
  • LOCAL_C_INCLUDES:C 或 C++ 语言需要的头文件的路径。
  • LOCAL_STATIC_LIBRARIES:当前模块依赖的静态库。
  • LOCAL_SHARED_LIBRARIES:当前模块依赖的动态库。
  • LOCAL_CFLAGS:提供给 C/C++ 编译器的额外编译参数。
  • LOCAL_JAVA_LIBRARIES:当前模块依赖的 Java 共享库。
  • LOCAL_STATIC_JAVA_LIBRARIES:当前模块依赖的 Java 静态库。
  • LOCAL_CERTIFICATE: APK 签名类型。
  • LOCAL_DEX_PREOPT: 禁止对当前模块进行预优化。
  • LOCAL_JACK_ENABLED: 禁止使用Jack编译工具链编译该模块。
  • LOCAL_MODULE_TAGS:当前模块所包含的标签,一个模块可以包含多个标签。标签的值可能是 user,eng, debug, optional。其中,optional 是默认标签。标签是提供给编译类型使用的。具体的规则见上面的 2.2 小节(产品级(Product)的参数配置 )中对编译类型的解析。 在 Android 10 中取消了 eng, debug 这两种类型,具体说明见路径 Android/build/make/Changes.md 中的说明。

除此以外,Build 系统中还定义了一些便捷的函数以便在 Android.mk 中使用,包括:

  • $(call all-java-files-under, ):获取指定目录下的所有 Java 文件。
  • $(call all-c-files-under, ):获取指定目录下的所有 C 语言文件。
  • $(call all-Iaidl-files-under, ) :获取指定目录下的所有 AIDL 文件。
  • $(call all-makefiles-under, ):获取指定目录下的所有 Make 文件。

定义完 LOCAL_XXX 变量之后, 最后一步是 include $(BUILD_XXX)
其中 BUILD_XXX 的可选项在 build/make/core/config.mk 文件中有定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk
BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk
BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
BUILD_HEADER_LIBRARY:= $(BUILD_SYSTEM)/header_library.mk
BUILD_AUX_STATIC_LIBRARY:= $(BUILD_SYSTEM)/aux_static_library.mk
BUILD_AUX_EXECUTABLE:= $(BUILD_SYSTEM)/aux_executable.mk
BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk
BUILD_PHONY_PACKAGE:= $(BUILD_SYSTEM)/phony_package.mk
BUILD_RRO_PACKAGE:= $(BUILD_SYSTEM)/build_rro_package.mk
BUILD_HOST_PREBUILT:= $(BUILD_SYSTEM)/host_prebuilt.mk
BUILD_PREBUILT:= $(BUILD_SYSTEM)/prebuilt.mk
BUILD_MULTI_PREBUILT:= $(BUILD_SYSTEM)/multi_prebuilt.mk
BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk
BUILD_STATIC_JAVA_LIBRARY:= $(BUILD_SYSTEM)/static_java_library.mk
BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
...

可见 BUILD_XXX 变量都是一个 mk 文件路径,每一个 BUILD_XXX 定义了一种编译规则。
当我们 include $(BUILD_XXX) ,编译系统就根据前面所定义的 LOCAL_XXX 变量,来定义模块的目标, 依赖关系,及编译命令,编译参数等。
需要注意的是,这个时候只是定义了模块的目标而已,并没有开始编译。

2.3.3 Android.bp

对于 Android.bp 模块来说,四个基本要素的配置方法如下:

1
2
3
4
5
6
7
cc_binary {              // 模块类型name: "hello",       // 模块名srcs: ["hello.cpp"], // 源文件列表shared_libs: [       // 依赖关系"liblog",],
}

Android.bp 支持的模块类型基本上跟 Android.mk 是一样的。它们的对应关系如下(Android/build/soong/androidmk/cmd/androidmk/android.go):

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
var moduleTypes = map[string]string{"BUILD_SHARED_LIBRARY":        "cc_library_shared","BUILD_STATIC_LIBRARY":        "cc_library_static","BUILD_HOST_SHARED_LIBRARY":   "cc_library_host_shared","BUILD_HOST_STATIC_LIBRARY":   "cc_library_host_static","BUILD_HEADER_LIBRARY":        "cc_library_headers","BUILD_EXECUTABLE":            "cc_binary","BUILD_HOST_EXECUTABLE":       "cc_binary_host","BUILD_NATIVE_TEST":           "cc_test","BUILD_HOST_NATIVE_TEST":      "cc_test_host","BUILD_NATIVE_BENCHMARK":      "cc_benchmark","BUILD_HOST_NATIVE_BENCHMARK": "cc_benchmark_host","BUILD_JAVA_LIBRARY":             "java_library_installable", // will be rewritten to java_library by bpfix"BUILD_STATIC_JAVA_LIBRARY":      "java_library","BUILD_HOST_JAVA_LIBRARY":        "java_library_host","BUILD_HOST_DALVIK_JAVA_LIBRARY": "java_library_host_dalvik","BUILD_PACKAGE":                  "android_app","BUILD_CTS_EXECUTABLE":          "cc_binary",               // will be further massaged by bpfix depending on the output path"BUILD_CTS_SUPPORT_PACKAGE":     "cts_support_package",     // will be rewritten to android_test by bpfix"BUILD_CTS_PACKAGE":             "cts_package",             // will be rewritten to android_test by bpfix"BUILD_CTS_TARGET_JAVA_LIBRARY": "cts_target_java_library", // will be rewritten to java_library by bpfix"BUILD_CTS_HOST_JAVA_LIBRARY":   "cts_host_java_library",   // will be rewritten to java_library_host by bpfix
}var prebuiltTypes = map[string]string{"SHARED_LIBRARIES": "cc_prebuilt_library_shared","STATIC_LIBRARIES": "cc_prebuilt_library_static","EXECUTABLES":      "cc_prebuilt_binary","JAVA_LIBRARIES":   "java_import","ETC":              "prebuilt_etc",
}

每一种模块类型都有很多配置选项,这里就不一一列举了,请参考 soong官方文档

需要注意的是目前 Android.bp 还不支持预置APK模块。所以我们还是需要 Android.mk 来定义预置 APK。

Android编译系统介绍相关推荐

  1. 第1章Android的系统介绍

    第1章Android的系统介绍 1.1 系统介绍    Android 是Google开发的基于Linux平台的.开源的.智能手机操作系统.Android包括操作系统.中间件和应用程序,由于源代码开放 ...

  2. Xamarin.Android编译CPU类型选择方式

    Xamarin.Android编译CPU类型选择方式 在Xamarin.Android编译的时候,默认提供了5种CPU类型供大家选择.它们分别为armeabi.armeabi-v7a.arm64-v8 ...

  3. Xamarin.Android编译提示找不到mscorlib.dll.so文件

    Xamarin.Android编译提示找不到mscorlib.dll.so文件 错误信息:AOT module 'mscorlib.dll.so' not found: Cannot load lib ...

  4. 【错误记录】Android 编译时技术报错 ( 注解处理器 process 方法多次调用问题 )

    文章目录 一.报错信息 二.问题分析 三.解决方案 注解处理器 AbstractProcessor 中的 process 方法可能会调用多次 , 在生成代码时 , 一定要注意 , 检测到 注解节点 后 ...

  5. 【错误记录】Android 编译时技术版本警告 ( 注解处理器与主应用支持的 Java 版本不匹配 )

    文章目录 一.报错信息 二.问题分析 三.解决方案 一.报错信息 在使用 Android 编译时技术 , 涉及 编译时注解 , 注解处理器 ; 开发注解处理器后 , 编译报如下警告 ; 该警告不会影响 ...

  6. android 模块不编译错误,Android 编译出错版本匹配问题解决办法

    Android 编译出错版本匹配问题解决办法 解决问题的关键在于版本匹配, compileSdkVersion compileSdkVersion targetSdkVersion 这三个参数的整数值 ...

  7. android编译error, forbidden warning出错问题解决

    android编译Kernel时,从高版本GCC起,就开始把warning作为error对待,比如我们遇到: gsl3670.c:2065:21: warning: unused variable ' ...

  8. android编译全过程

    android编译全过程 (2011-06-04 15:27) 标签:  android编译  android 模块编译,mm 命令 如果你只需要修改某一个模块的内容,但是却每次都要执行make, 最 ...

  9. Android编译系统分析四:实战-新增一个产品

    通过上一节"android编译系统(三)-make"的分析,初步理清楚了编译初期加载产品相关信息的流程,整个过程主要涉及三个文件:1.AndroidProducts.mk,2.具体 ...

最新文章

  1. 运维企业部分学习开篇
  2. linux下diff、patch制作补丁
  3. laravel debug,http模式,日志
  4. SQL语言学习(四)常见函数学习
  5. MOS和三极管不同接法对应的性能比较
  6. 安卓开发工程师面试题!春招我借这份PDF的复习思路,不吃透都对不起自己
  7. POJ 2449 Remmarguts' Date (SPFA + A星算法) - from lanshui_Yang
  8. android修改系统分辨率,Android4.2修改系统分辨率的方法
  9. pytorch模型转onnx Exporting the operator _thnn_fused_lstm_cell to ONNX opset version 9 is not supported
  10. All the python knowledge that I come across
  11. bat批处理实现win和linux间上传下载
  12. Q绑查询HTML源码
  13. 基于全基因组的基因家族分析的初尝试
  14. leakCanary检测内存泄漏的原理
  15. java 增加内存_如何增加java虚拟机可以使用的最大内存
  16. 国内计算机类APP相关竞赛总结
  17. iQQ 学习笔记3 :编写代码打包Ant脚本
  18. android studio 运行闪退,ubuntu上在androidstudio中启动emulator闪退的解决方法
  19. 浅谈安全管理平台中的事件关联分析
  20. 像电影里黑客高手一样敲代码攻击入侵网站(模拟)

热门文章

  1. python 学习笔记(1)-转载
  2. myeclipse/eclipse方法和类的自动注解
  3. ubuntu 装完系统之后的简单配置
  4. LoadRunner参数化---数据文件属性 之 数据分配方法和数据更新方法
  5. 常去阅读 12个开发人员的博客
  6. Python 3.4中文编码
  7. leecode - 入门 -- 双指针总结
  8. 【数据结构】栈、队列、堆的python实现
  9. linux b权限开头的文件夹,linux文件权限与目录配置
  10. box怎么用 latency_box-sizing使用场景