🗒️Android编译原理之编译步骤
2023-8-18
| 2024-11-22
字数 4330阅读时长 11 分钟
type
status
date
slug
summary
tags
category
icon
password
URL

一、概述

在 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来进行构建工作。
编译速度快了一些,但是既然要干, 那就干个大的,最终目标要把make都取代,于是从Android8.0开始,Google为了进一步淘汰Makefile,因此引入了Android.bp文件来替换之前的Android.mk。
Android.bp只是一个纯粹的配置文件,不包括分支、循环语句等控制流程,本质上就是一个json配置文件。Android.bp 通过Blueprint+soong转换成ninja的构建规则文件build.ninja,再使用ninja来进行构建工作。
Android10.0上,mk和bp编译的列表可以从 out/.module_paths中的Android.bp.list、Android.mk.list中看到,Android10.0还有400多个mk文件没有被替换完,Google任重道远。
Android编译演进过程:
Android7.0之前 使用GNU Make
Android7.0 引入ninja、kati、Android.bp和soong构建系统
Android8.0 默认打开Android.bp
Android9.0 强制使用Android.bp
Google在 Android 7.0之后,引入了Soong构建系统,旨在取代make,它利用 Kati GNU Make 克隆工具和 Ninja 构建系统组件来加速 Android 的构建。
Make 构建系统得到了广泛的支持和使用,但在 Android 层面变得缓慢、容易出错、无法扩展且难以测试。Soong 构建系统正好提供了 Android build 所需的灵活性。
notion image

二、编译流程

2.1 编译构成

Android的编译目录在build 中,看一下源码中的build目录,现在是这个样子
notion image
这个目录中可以看到core文件夹被link到了make/core,envsetup.sh被link到make/envsetup.sh,这主要是为了对使用者屏蔽切换编译系统的差异。
这里重点看四个文件夹:blueprint、kati、make、soong
blueprint
用于处理Android.bp,编译生成*.ninja文件,用于做ninja的处理
kati
用于处理Android.mk,编译生成*.ninja文件,用于做ninja的处理
make
文件夹还是原始的make那一套流程,比如envsetup.sh
soong
构建系统,核心编译为soong_ui.bash
Soong编译系统家族成员及各自关系如下图所示:
notion image
在编译过程中,Android.bp会被收集到out/soong/build.ninja.d,blueprint以此为基础,生成out/soong/build.ninja
Android.mk会由kati/ckati生成为out/build-aosp_arm.ninja
两个ninja文件会被整合进入out/combined-aosp_arm.ninja

2.2 编译步骤

三、编译工具链

编译系统中,涉及以下一些工具链,由这些工具链相辅相成,才最终编译出了我们所需要的镜像版本。
prebuilts/build_tools/linux-x86/bin/ninja

3.1 soong说明

Soong 构建系统是在 Android 7.0 (Nougat) 中引入的,旨在取代 Make。它利用 Kati GNU Make 克隆工具和 Ninja 构建系统组件来加速 Android 的构建。
Soong是由Go语言写的一个项目,从Android 7.0开始,在prebuilts/go/目录下新增了Go语言所需的运行环境,Soong在编译时使用,解析Android.bp,将之转化为Ninja文件,完成Android的选择编译,解析配置工作等。故Soong相当于Makefile编译系统的核心,即build/make/core下面的内容。
另外Soong还会编译产生一个androidmk命令,可以用来手动将Android.mk转换成Android.bp文件。不过这只对无选择、循环等复杂流程控制的Android.mk生效。
soong脚本和代码目录:/build/soong

3.2 kati说明

kati是一个基于Makefile来生成ninja.build的小项目。主要用于把Makefiel转成成ninja file,自身没有编译能力,转换后使用Ninja编译。
在编译过程中,kati负责把既有的Makefile、Android.mk文件,转换成Ninja文件。在Android 8.0以后,它与Soong一起,成为Ninja文件的两大来源。Kati更像是Google过渡使用的一个工具,等所有Android.mk都被替换成Android.bp之后,Kati有可能退出Android编译过程.
在单独使用时,它对普通的小项目还能勉强生效。面对复杂的、多嵌套的Makefile时,它往往无法支持,会出现各种各样的问题。当然,也可以理解为,它只为Android而设计。
kati脚本和代码目录:/build/kati

3.3 blueprint说明

Blueprint由Go语言编写,是生成、解析Android.bp的工具,是Soong的一部分。Soong则是专为Android编译而设计的工具,Blueprint只是解析文件的形式,而Soong则解释内容的含义。
在Android编译最开始的准备阶段,会执行build/soong/soong_ui.bash进行环境准备。
对blueprint项目编译完成之后会在out/soong/host/linux-x86/bin目录下生成soong编译需要的5个执行文件(bpfix,bpfmt,bpmodify,microfatory,bpmodify)。
Soong是与Android强关联的一个项目,而Blueprint则相对比较独立,可以单独编译、使用。
blueprint代码目录:/build/blueprint

3.4 ninja说明

Ninja是一个致力于速度的小型编译系统(类似于Make),如果把其他编译系统比做高级语言的话,Ninja就是汇编语言。通常使用Kati或soong把makefile转换成Ninja files,然后用Ninja编译。
主要两个特点:
1)可以通过其他高级的编译系统生成其输入文件;
2)它的设计就是为了更快的编译;
ninja核心是由C/C++编写的,同时有一部分辅助功能由python和shell实现。由于其开源性,所以可以利用ninja的开源代码进行各种个性化的编译定制。
从Android 7开始,编译时默认使用Ninja。但是,Android项目里是没有.ninja文件的。遵循Ninja的设计哲学,编译时,会先把Makefile通过kati转换成.ninja文件,然后使用ninja命令进行编译。这些.ninja文件,都产生在out/目录下,共有三类:
  1. build-*.ninja文件,通常非常大,几十到几百MB。对make全编译,命名是build-<product_name>.ninja。如果Makefile发生修改,需要重新产生Ninja文件。
    1. mm、mma的Ninja文件,命名是build-<product_name>-<path_to_Android.mk>.ninja。而mmm、mmma的Ninja文件,命名是build-<product_name>-_<path_to_Android.mk>.ninja。
  1. combined-*.ninja文件。在使用了Soong后,除了build-*.ninja之外,还会产生对应的combined-*.ninja,二者的*内容相同。这类是组合文件,是把build-*.ninja和out/soong/build.ninja组合起来。所以,使用Soong后,combined-*.ninja是编译执行的真正入口。
  1. out/soong/build.ninja文件,它是从所有的Android.bp转换过来的
build-*.ninja是从所有的Makefile,用Kati转换过来的,包括build/core/*.mk和所有的Android.mk。所以,在不使用Soong时,它是唯一入口。在使用了Soong以后,会新增源于Android.bp的out/soong/build.ninja,所以需要combined-*.ninja来组合一下。

3.5 工具链的关系

Android.mk文件、Android.bp、kati、Soong、Blueprint、Ninja之间的关系如下:

四、编译步骤之envsetup.sh

notion image

4.1 validate_current_shell

确定当前的shell环境,建立shell命令

4.2 source_vendorsetup

4.3 addcompletions

五、编译步骤之lunch

5.1 lunch说明

环境变量初始化完成后,我们需要选择一个编译目标。lunch 主要作用是根据用户输入或者选择的产品名来设置与具体产品相关的环境变量。
如果你不知道想要编译的目标是什么,直接执行一个lunch命令,会列出所有的目标,直接回车,会默认使用aosp_arm-eng这个目标。
参数
说明
PLATFORM_VERSION_CODENAME=REL
表示平台版本的名称
PLATFORM_VERSION=13
Android平台的版本号
TARGET_PRODUCT=miodm_topaz_native
所编译的产品名称
TARGET_BUILD_VARIANT=userdebug
所编译产品的类型
TARGET_BUILD_TYPE=release
编译的类型,debug和release
TARGET_ARCH=arm64
表示编译目标的CPU架构
TARGET_ARCH_VARIANT=armv8-a
表示编译目标的CPU架构版本
TARGET_CPU_VARIANT=generic
表示编译目标的CPU代号
HOST_ARCH=x86_64
表示编译平台的架构
HOST_2ND_ARCH=x86
HOST_OS=linux
表示编译平台的操作系统
HOST_OS_EXTRA=linux-5.4.0-124-generic-x86_64-Ubuntu-18.04.6-LTS
编译系统之外的额外信息
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_BUILD_TYPE=release
BUILD_ID=TKQ1.220710.001
BUILD_ID会出现在版本信息中,可以利用
OUT_DIR=out
编译结果输出的路径

5.2 lunch

lunch指令是在envsetup.sh中被定义的。主要就是用来设置 TARGET_PRODUCT、TARGET_BUILD_VARIANT、TARGET_PLATFORM_VERSION、TARGET_BUILD_TYPE、TARGET_BUILD_APPS等环境变量
lunch操作流程如下:
  1. 获取lunch操作的参数,如果参数不为空,参数则为指定要编译的设备型号和编译类型;如果参数为空,会调用print_lunch_menu来显示Lunch菜单项,读取用户的输入,存入answer
  1. 如果answer为空,即之前在lunch菜单用,用户只敲了一个回车。会将默认选项改为aosp_arm-eng,结果存入selection
  1. 如果lunch操作得到的输入是数字,则将数字转换为LUNCH_MENU_CHOICES中的字符串,结果存入selection
  1. 解析selection的值,得到product = aosp_arm 和variant = eng, 把他们分别保存到TARGET_PRODUCT 和 TARGET_BUILD_VARIANT 中
  1. 根据前面的设置,调用build_build_var_cache 来更新编译环境相关变量
  1. export 编译选项TARGET_PRODUCT, TARGET_BUILD_VARIANT和TARGET_BUILD_TYPE三元组
  1. 调用set_stuff_for_environment 来设置其他环境变量,如PROMPT_COMMAND,编译toolchain和tools相关的路径等
  1. 调用printconfig 来输出当前的设置选项

5.3 build_build_var_cache

根据前面的设置,更新编译环境相关变量
主要通过执行 "build/soong/soong_ui.bash --dumpvars-mode" 完成
最终执行的是 "./out/soog_ui --dumpvars-mode"
soong_ui 由build/soong/cmd/soong_ui/main.go编译生成
最后调用到了ckati执行-f build/make/core/config.mk
下面我们单独研究一下config.mk。

5.4 config.mk

说明:config.mk首先加载了build/make/common 中的core.mk、math.mk、strings.mk、json.mk 用来配置一些shell环境、math函数、string和json的一些支持函数。最主要的操作还是加载build/make/core中的envsetup.mk和dumpvar.mk

5.4.1 build/make/core/envsetup.mk

envsetup.mk 主要加载了product_config.mk和board_config.mk,用来得到TARGET_DEVICE和其他变量。

5.4.2 build/make/core/product_config.mk

5.4.3 build/make/core/board_config.mk

板级可以在$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)下定义,也可以在vendor/*/$(TARGET_DEVICE)下定义。
在这两个地方搜索,但要确保只存在一个。真正的板级应始终与OEM vendor相关联。``

六、总结

至此,envsetup.sh 和lunch()的初始化流程基本上理清了,主要就是加载了环境变量,并选择了编译目标,后面只要执行一下make就能够进行启动编译,下一节让我们一起看看敲下make后到底发生了什么。
notion image
  • andorid
  • 编译原理
  • 高通Android启动代码流程分析(SBL->ABL)Android编译原理之make编译过程
    Loading...