Android Build System剖析
Android预构建应用是如何制作的,背后的构建系统又是什么?
本文旨在分享关于Android构建系统以及与原始设备制造商(OEM)集成的知识,简化理解AOSP复杂机制的过程。与手动查阅各种文件及其内部工作流程相比,本文可以作为进入AOSP构建系统领域的快速指南。不涉及Gradle构建系统,Android平台构建系统与基于Gradle的Android应用构建系统截然不同,值得一提的是,大家是否曾想过Android预构建应用是如何制作的,背后的构建系统又是什么?对于大多数Android开发者来说,寻找关于Android操作系统核心和内部运行机制的优质资源是一个共同的痛点。Framework的开发并不容易,此外,构建一个带有其复杂性的Framework应用可能会令人困惑。
本文要点
在本文结尾,您将理解并了解有关Android平台构建系统的所有复杂性、功能、定义和关联。以下是四个详细讨论的要点:
- 环境设置
- 运行
source build/envsetup.sh
- 运行
- 选择要构建的目标产品
- 运行
lunch <option>
- 运行
- 构建代码
- 运行
make <module-name>
或m <module-name>
- 运行
- 在设备上运行/刷写/安装
- 通过
adb push
推送apk - 通过
fastboot
刷写镜像
- 通过
但在深入探讨以上4个步骤之前,有一些先决条件:
A. Android构建系统的演进
B. Android.bp
和blueprint
C. 下载AOSP源代码
A. Android构建系统的演进
- Android N版本(v7.0)之前,
编译是由GNU make工具完成的。所有规则都写在一个名为Makefile的配置文件中,make工具会根据Makefile中的指令编译代码。
什么是make? make是一种自动化构建工具,通过读取Makefile将源代码自动编译为可执行程序和库文件。
什么是Makefile或.mk文件? Makefile定义了目标程序的依赖关系和生成目标程序的相关规则。
就像Gradle有build.gradle
一样,make有Makefile(main.mk)。
- 在Android N版本(v7.0)之后,
在Android层面,GNU Make编译变得缓慢、容易出错、不可扩展且难以测试,因此Google引入了Ninja构建系统,该系统通过.ninja文件实现了Android构建系统的灵活性,因为.ninja
文件是人类可读的。
什么是ninja? 它是一个编译框架,将根据相应的.bp(blueprint)文件编译成.ninja
文件。通常情况下,.ninja
文件不会手动修改,而是通过将.bp(blueprint)
转换为.ninja
文件来编译.ninja
文件。
什么是soong和.bp文件? 为了生成.ninja
文件,Google引入了soong构建系统,其中包括一个名为blueprinting
的工具,用于将Android.bp
文件解析为.ninja
文件,并引入了kati工具,用于将Android.mk
文件转换为.ninja
文件。
什么是kati? 这是一个基于Golang和C++的工具,其主要功能是将复杂的Android.mk文件转换为ninja文件。
附:Soong还编译并生成了一个androidmk
命令,将Android.mk文件转换为Android.bp文件。
就像Gradle有build.gradle
一样,ninja有build.ninja
。
- 在2020年的Android发行说明中,Google表示他们将开始将构建系统迁移到Bazel。
B. 详细了解Android.bp和blueprinting
在Android.bp中,我们会根据模块类型构建所需内容。
常用的类型和方法如下:
android_app
:用于构建apk,其功能与Android.mk的BUILD_PACKAGE相同。
java_library
:从.class文件生成.jar包。生成的jar包不适合直接在设备上安装,而是将其用作static_libs依赖项。
static_libs
:在编译时由调用者解析的库,并由编译器复制到目标应用程序中。
android_library
:将源代码与Android资源文件一起链接到设备的.jar文件中。
android_library
具有不同的变体,从.class
文件生成.jar
包,以及从aapt2
生成的名为package-res.apk
的文件。生成的apk文件不能直接在设备上安装,但可以用作android_app
模块的static_libs
依赖项。
platform_apis
:使用SDK的隐藏API进行编译
certificate
:指定要使用的签名,如上所述,使用平台签名。
android_library_import
:将Android存档(aar)导入到android_app
,必须将其用作android_app
模块的static_libs
依赖项。
让我们尝试创建一个名为GlanceApp的随机示例应用程序,以下是示例Android.bp文件,在GlanceApp模块中,将 glance_android_library
引入为静态依赖项, 而glance_android_library
又将glance_java_library
和content_aar_plugin
作为依赖项, 以生成GlanceApp.apk
。
java_library {
name: "glance_java_library",
srcs: [
"src/com/android/glance/file/**.java",
],
jarjar_rules: ":jarjar-rules-shared",
}
android_library {
name: "glance_android_library",
manifest: "tests/AndroidManifest-base.xml",
additional_manifests: ["tests/AndroidManifest.xml"],
resource_dirs: [
"res",
],
srcs: [
"src/**/*.kt",
"src/**/*.java",
],
static_libs: [
"glance_java_library",
"content_aar_plugin",
"glide-annotation-and-compiler-prebuilt",
],
libs: [
"android.test.base",
],
kotlincflags: ["-Xjvm-default=enable"],
aaptflags: [
"--extra-packages",
],
plugins: ["dagger2-compiler","glide-annotation-processor"],
}
android_app {
name: "GlanceApp",
static_libs: [
"glance_android_library",
],
resource_dirs: [],
platform_apis: true,
system_ext_specific: true,
certificate: "platform",
privileged: true,
kotlincflags: ["-Xjvm-default=enable"],
dxflags: ["--multi-dex"],
required: [ "privapp_whitelist_com.android.glance",],
aaptflags: ["--auto-add-overlay",],
platform_apis: true,
certificate: "platform",
optimize: {enabled: false,},
sdk_version: "core_platform",
}
android_library_import {
name: "content_aar_plugin",
aars: ["libs/content_aar_plugin.aar"],
static_libs: ["androidx-constraintlayout_constraintlayout",]
}
因此,将content_aar_plugin
添加到您的应用程序中,同时还将glide(第三方库)作为依赖项添加。
由于我们已将privileged
设置为true,并将证书设置为platform,GlanceApp将充当系统特权应用程序。
C. 下载AOSP源码
如果你还没有下载AOSP源码,请使用下面命令下载源码:
mkdir android-13.0.0_r12
cd android-13.0.0_r12
repo init --depth=1 -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r12
repo sync --force-sync --no-clone-bundle --no-tags -j$(nproc)
该流程图说明了以下内容:
- 从
Android.mk
和其他Makefiles生成out/build-aosp_bluejay.ninja
文件。 - 从Android.bp生成
out/soong/build.ninja
文件。此外,还会生成一个较小的out/combined-aosp_bluejay.ninja
文件,负责将两者连接起来,作为执行入口。 - 最终,Ninja文件是真正直接控制源代码编译的工具,负责生成apk、aar和dex文件。APK的签名也是使用ninja规则完成的,然后完成这一切后,会创建
*.imgs
文件。
最后,让我们详细讨论这3个步骤。
- 运行
source build/envsetup.sh
- 选择
lunch
选项 - 运行
make <module-name>
或m <module-name>
Android编译的第一步
是运行source build/envsetup.sh
。
这为后续的编译步骤铺平了道路。了解envsetup.sh
与我们的分析对应关系至关重要。
envsetup.sh
脚本定义了许多函数。在执行此脚本后,您可以使用Linux命令在当前控制台直接执行这些函数,比如lunch、mm、mmm
等(使用hmm查看所有可用命令)。因此,lunch
和mmm
实际上是shell函数,相当于shell脚本。让我们讨论一下在build/envsetup.sh
中实现这些命令的原理。
Android编译的第二步
是运行lunch
命令。envsetup.sh
脚本中的lunch()
函数设置用于构建图像(*.imgs)和其他构件(如apk、jar、.so等)的环境。它从名为AndroidProducts.mk.list
的文件列表中读取目标设备的列表。当调用print_lunch_menu()
函数时,它会从AndroidProducts.mk.list
中获取COMMON_LUNCH_CHOICES
变量并显示给用户。从该函数中,将调用get_build_var()
,然后调用build/soong/soong_ui.bash --dumpvar-mode
。
soong_ui.bash
会调用/build/soong/cmd/soong_ui/main.go
中的main()
函数,该函数会调用build.FindSources(buildCtx, config, f)
,从而找到所有的AndroidProducts.mk
,并制作一个带有单个COMMON_LUNCH_CHOICES
的AndroidProducts.mk.list
。请查看以下代码段…
# lunch.bash
function print_lunch_menu()
{
...
choices=$(TARGET_BUILD_VARIANT= get_build_var COMMON_LUNCH_CHOICES 2>/dev/null)
...
}
function get_build_var()
{
...
build/soong/soong_ui.bash --dumpvar-mode $1)
}
func main() {
...
config := c.config(buildCtx, args...)
...
f := build.NewSourceFinder(buildCtx, config)
defer f.Shutdown()
build.FindSources(buildCtx, config, f)
...
}
function lunch()
{
print_lunch_menu
...
read selection
...
product=${selection%%-*} # Trim everything after first dash
variant_and_version=${selection#*-} # Trim everything up to first dash
if [ "$variant_and_version" != "$selection" ]; then
variant=${variant_and_version%%-*}
if [ "$variant" != "$variant_and_version" ]; then
version=${variant_and_version#*-}
fi
fi
...
TARGET_PRODUCT=$product \
TARGET_BUILD_VARIANT=$variant \
TARGET_PLATFORM_VERSION=$version \
export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT)
export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT)
export TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION)
...
}
不常见但是重要的命令
-
set_stuff_for_environment
:其功能是将一些路径添加到PATH环境变量中,以便我们可以在已执行lunch的控制台中使用Android源代码中的一些其他工具,例如模拟器。 -
tapas
:它为构建未捆绑的应用程序或APK(正常的Android包)设置构建环境。您可以选择芯片架构、构建变体以及模块构建所需的应用程序。例如:tapas SystemUI arm eng
-
banchan
:它为构建未捆绑的模块或APEX(本地服务、库、HAL等)设置构建环境。您可以选择芯片架构、构建变体以及模块构建所需的应用程序。例如:banchan com.android.boinic SystemUI arm eng
-
make命令
如果您理解了lunch命令的原理,这些命令将变得非常容易。
m
:从树的顶部进行编译。例如:m
或m <module-name>
mm
:构建并安装当前目录中的所有模块及其依赖项。mmm
:构建并安装所提供目录中的所有模块及其依赖项。要限制构建的模块,可以使用以下语法:mmm dir/:target1,target2
。例如:mmm packages/apps/Launcher3
还有其他几个支持的命令,可以使用hmm
命令查看列表。
Android编译的第三步
make
或m
在Android N之前
m或make命令相当于make -f build/core/main.mk
(通过GNU make构建)
目前
m或make命令相当于build/soong/soong_ui.bash -make-mode
,这意味着soong_ui.bash
是Android平台构建系统的核心。
# song_ui.bash
source ${TOP}/build/soong/scripts/microfactory.bash
soong_build_go soong_ui android/soong/cmd/soong_ui
soong_build_go mk2rbc android/soong/mk2rbc/cmd
soong_build_go rbcrun rbcrun/cmd
cd ${TOP}
exec "$(getoutdir)/soong_ui" "$@"
可以看出,soong_ui.bash
的主要逻辑分为4部分:
运行microfactory
,为构建Go脚本设置环境。帮助我们使用soong_build_go
构建所请求的二进制文件。
soong_build_go soong_ui
准备了shell函数调用并执行了soong的入口即main.go
。
soong_build_go mk2rbc
和rbcrun
执行了bazel构建系统的入口,并将不同的Makefile转换为Starlark
。
最终执行soong_ui
。
因此,从第2点我们理解了soong的入口即main.go
请注意:第3点超出了本文的范围。
在main.go
中,必须采用以下4个参数之一:
--dumpvar-mode
--dumpvars-mode
--make-mode
--build-mode
前两个参数使用较少且不用于构建/制作,因此我们将跳过这些,讨论下面的 --make-mode
和--build-mode
。
func main() {
...
if os.Args[1] == " --dumpvar-mode" {
dumpVar(buildCtx, config, os.Args[2:])
} else if os.Args[1] == " --dumpvars-mode" {
dumpVars(buildCtx, config, os.Args[2:])
} else {
//build --make-mode and --build-mode
if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
clean(ctx, config)
return
}
if inList("help", config.Arguments())) {
help(ctx, config)
return
}
...
}
}
还有其他几个参数,如clobber/clean
和help
,它们调用cleanbuild.go
和help.sh
。clobber/clean
用于删除输出文件夹,help
用于显示命令的用户手册。
接下来,让我们深入探讨 --make-mode
和 --build - mode
。
以下是构建过程的高级流程,我们将逐个讨论每个连接点。
main.go
通过config.go
设置配置,传递参数如skip等。然后,FindSources函数查找要合并到kati.go
的mk文件和要处理并转换为ninja文件的soong.go
的bp文件,以及借助ninja.go
。只有在启用了bazel
时,才需要bp2build.go
,然后需要将.bp文件转换为BAZEL文件。对于本文,我们假设未启用bazel
。
# main.go
function main() {
c, args, err := getCommand(os.Args)
...
buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Context: ctx,
Logger: log,
Metrics: met,
Tracer: trace,
Writer: output,
Status: stat,
}}
config := c.config(buildCtx, args...)
...
build.FindSources(buildCtx, config, f)
...
c.run(buildCtx, config, args, logsDir)
}
Config会将参数发送到build.go
,以决定要运行什么以及以什么顺序运行。例如,如果我们运行以下命令:
build/soong/soong_ui.bash --make-mode --skip-ninja
Config的skipNinja将为true。然后继续进行:
//config.go
func config parseArgs(ctx Context, args []string) {
for i := 0; i < len(args); i++ {
arg := strings.TrimSpace(args[i])
...
if arg == "--skip-ninja" {
c.skipNinja = true
} else if arg == "--skip-make" {
c.skipConfig = true
c.skipKati = true
} else if arg == "--skip-kati" {
c.skipKati = true
} else if arg == "--soong-only" {
c.skipKati = true
c.skipKatiNinja = true
...
c.arguments = append(c.arguments, arg)
}
}
}
一旦配置设置完成,在构建流程中,默认情况下变量“what”被设置为runAll
。
//build.go
func Build(ctx Context, config Config) {
...
what := RunAll
...
if config.SkipKati() {
ctx.Verboseln("Skipping Kati as requested")
what = what &^ RunKati
}
if config.SkipKatiNinja() {
ctx.Verboseln("Skipping use of Kati ninja as requested")
what = what &^ RunKatiNinja
}
if config.SkipSoong() {
ctx.Verboseln("Skipping use of Soong as requested")
what = what &^ RunSoong
}
if config.SkipNinja() {
ctx.Verboseln("Skipping Ninja as requested")
what = what &^ RunNinja
}
...
if what&RunSoong != 0 {
runSoong(ctx, config)
}
if what&RunKati != 0 {
genKatiSuffix(ctx, config)
runKatiCleanSpec(ctx, config)
runKatiBuild(ctx, config)
runKatiPackage(ctx, config)
ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0666) // a+rw
}
...
if what&RunNinja != 0 {
if what&RunKati != 0 {
installCleanIfNecessary(ctx, config)
}
runNinjaForBuild(ctx, config)
}
// Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last.
if what&RunBazel != 0 {
runBazel(ctx, config)
}
}
一旦所有元过滤器完成,比如跳过 Kati、跳过 Ninja 等,Soong 进程就会开始。
Soong 的流程
有一个名为 bootstrap 的工具,它读取描述自身的 Blueprint 文件,并生成描述如何构建其完整版本的 .bootstrap/build.ninja
文件,并使用它来生成 Soong 发出的最终 Ninja 文件。
//song.go
func runSoong(ctx Context, config Config) {
...
buildMode := config.bazelBuildMode()
integratedBp2Build := buildMode == mixedBuild
...
bootstrapBlueprint(ctx, config)
...
ninja := func(name, ninjaFile string, targets ...string) {
...
ninjaArgs := []string{
"-d", "keepdepfile",
"-d", "stats",
"-o", "usesphonyoutputs=yes",
"-o", "preremoveoutputs=yes",
"-w", "dupbuild=err",
"-w", "outputdir=err",
"-w", "missingoutfile=err",
"-j", strconv.Itoa(config.Parallel()),
"--frontend_file", fifo,
"-f", filepath.Join(config.SoongOutDir(), ninjaFile),
}
...
}
...
if config.Bp2Build() {
targets = append(targets, config.Bp2BuildMarkerFile())
}
...
ninja("bootstrap", "bootstrap.ninja", targets...)
...
}
如果 Bp2Build
为 true,意味着需要使用 Bazel 构建系统,需要执行 bp2build.go
。它会编写等效于可使用 Bazel 构建的 Android.bp
文件的 .bzl
文件。
Google 希望将所有与编译相关的任务交给 Bazel。这是一个庞大的项目,而 Android 代码也很庞大。我不知道这个项目何时会完成。
从 Android.bp
生成 out/soong/build.ninja
。此后,将执行 Kati 来解析 Makefiles,但这不是 Soong 引导的一部分。
Kati 的流程
加载 Android.mk
在 build.go 中执行 runKati
,它调用文件中的流程(core/main.mk),以以下方式包括每个子目录的 Android.mk。
subdir_makefiles := $(SOONG_OUT_DIR)/installs-$(TARGET_PRODUCT).mk $(SOONG_ANDROID_MK)
# Android.mk files are only used on Linux builds, Mac only supports Android.bp
ifeq ($(HOST_OS),linux)
subdir_makefiles += $(file <$(OUT_DIR)/.module_paths/Android.mk.list)
endif
subdir_makefiles += $(SOONG_OUT_DIR)/late-$(TARGET_PRODUCT).mk
subdir_makefiles_total := $(words int $(subdir_makefiles) post finish)
.KATI_READONLY := subdir_makefiles_total
$(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk)))
基于这个逻辑,在编译时我们经常会看到 [xxx/xxx] 包含 xxx。在 Android 源代码中搜索文件,找到 Android.mk,并将相应的文件路径放入 AndroidProducts.mk.list
文件,然后为其生成 ninja 文件。它使用 ckati 来实现。
例如:命令:ckati,-f build/make/core/main.mk
从 Android.mk 和其他 Makefiles 中,将生成 out/build-<product_name>.ninja
文件。
我们来讨论一下生成的 ninja 文件是什么样的。以 SystemUI-core 为例。
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: SystemUI-core
# Variant: android_common
# Type: android_library
# Factory: android/soong/android.ModuleFactoryAdaptor.func1
# Defined: frameworks/base/packages/SystemUI/Android.bp:69:1
m.SystemUI-core_android_common.moduleDesc = //frameworks/base/packages/SystemUI:SystemUI-core
m.SystemUI-core_android_common.moduleDescSuffix = $ [common]
m.SystemUI-core_android_common.javacFlags = -Xlint:-dep-ann
m.SystemUI-core_android_common.kotlincFlags = -Xjvm-default=enable -Xsam-conversions=class -no-stdlib -no-jdk
rule m.SystemUI-core_android_common.aidl
command = rm -rf out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/gen/aidl/aidl0.tmp && mkdir -p out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/gen/aidl/aidl0.tmp && FLAGS=' -Iframeworks/base/core/java -Iframeworks/base/drm/java -Iframeworks/base/graphics/java -Iframeworks/base/identity/java -Iframeworks/base/keystore/java -Iframeworks/base/location/java -Iframeworks/base/lowpan/java -Iframeworks/base/media/java -Iframeworks/base/media/mca/effect/java -Iframeworks/base/media/mca/filterfw/java -Iframeworks/base/media/mca/filterpacks/java -Iframeworks/base/mms/java -Iframeworks/base/opengl/java -Iframeworks/base/rs/java -Iframeworks/base/sax/java -Iframeworks/base/telecomm/java -Iframeworks/base/telephony/java -Iframeworks/base/packages/SystemUI -Iframeworks/base/packages/SystemUI/src --min_sdk_version=current -Iframeworks/base/packages/SystemUI/' && out/host/linux-x86/bin/aidl -dout/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/gen/aidl/frameworks/base/packages/SystemUI/src/com/android/systemui/assist/IAssistHandleService.aidl.d $$FLAGS frameworks/base/packages/SystemUI/src/com/android/systemui/assist/IAssistHandleService.aidl out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/gen/aidl/aidl0.tmp/frameworks/base/packages/SystemUI/src/com/android/systemui/assist/IAssistHandleService.java && out/host/linux-x86/bin/soong_zip -srcjar -write_if_changed -o out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/gen/aidl/aidl0.srcjar -C out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/gen/aidl/aidl0.tmp -D out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/gen/aidl/aidl0.tmp && rm -rf out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/gen/aidl/aidl0.tmp # hash of input list: 9def94a8716a4acf60f436254037b1e48158f63767ed67940332f88206e735f1
restat = true
rule m.SystemUI-core_android_common.lint
command = out/host/linux-x86/bin/sbox --sandbox-path out/soong/.temp --output-dir out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/lint --manifest out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/lint.sbox.textproto
rspfile = out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/lint-srcs.list
rspfile_content = ${in}
build $
out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/manifest_fixer/AndroidManifest.xml: g.java.manifestFixer $
frameworks/base/packages/SystemUI/AndroidManifest.xml | ${g.android.soong.java.config.ManifestFixerCmd}
description = ${m.SystemUI-core_android_common.moduleDesc}fix manifest${m.SystemUI-core_android_common.moduleDescSuffix}
args = --library --targetSdkVersion 33 --minSdkVersion 33 --raise-min-sdk-version
build $
out/soong/.intermediates/development/samples/SystemUI/SystemUI/android_common/meta_lic: g.android.licenseMetadataRule | ${g.android.licenseMetadataCmd} || $
out/soong/.intermediates/build/soong/java/core-libraries/core-public-stubs-system-modules/android_common/meta_lic $
out/soong/.intermediates/build/soong/java/core-libraries/legacy.core.platform.api.stubs/android_common/meta_lic $
out/soong/.intermediates/external/apache-http/org.apache.http.legacy/android_common/meta_lic $
out/soong/.intermediates/frameworks/base/android_stubs_current/android_common/meta_lic $
out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/meta_lic out/soong/.intermediates/frameworks/base/ext/android_common/meta_lic $
out/soong/.intermediates/frameworks/base/framework/android_common/meta_lic $
out/soong/.intermediates/frameworks/base/test-base/android.test.base/android_common/meta_lic $
out/soong/.intermediates/frameworks/base/test-mock/android.test.mock/android_common/meta_lic $
out/soong/.intermediates/libcore/core-lambda-stubs/android_common/meta_lic $
out/soong/.intermediates/system/libhidl/transport/base/1.0/android.hidl.base-V1.0-java/android_common/meta_lic $
out/soong/.intermediates/system/libhidl/transport/manager/1.0/android.hidl.manager-V1.0-java/android_common/meta_lic
description = ${m.SystemUI_android_common.moduleDesc}license metadata${m.SystemUI_android_common.moduleDescSuffix}
args = -mt android_app -r development/samples/SystemUI -mc UNKNOWN -p "Android" -k SPDX-license-identifier-Apache-2.0 -c notice -n 'build/soong/licenses/LICENSE:Android' -d 'out/soong/.intermediates/build/soong/java/core-libraries/core-public-stubs-system-modules/android_common/meta_lic:dynamic' -d out/soong/.intermediates/build/soong/java/core-libraries/legacy.core.platform.api.stubs/android_common/meta_lic -d 'out/soong/.intermediates/external/apache-http/org.apache.http.legacy/android_common/meta_lic:dynamic' -d 'out/soong/.intermediates/frameworks/base/android_stubs_current/android_common/meta_lic:dynamic' -d 'out/soong/.intermediates/frameworks/base/android_stubs_current/android_common/meta_lic:dynamic' -d out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/meta_lic -d out/soong/.intermediates/frameworks/base/ext/android_common/meta_lic -d out/soong/.intermediates/frameworks/base/framework/android_common/meta_lic -d 'out/soong/.intermediates/frameworks/base/test-base/android.test.base/android_common/meta_lic:dynamic' -d 'out/soong/.intermediates/frameworks/base/test-mock/android.test.mock/android_common/meta_lic:dynamic' -d out/soong/.intermediates/libcore/core-lambda-stubs/android_common/meta_lic -d 'out/soong/.intermediates/libcore/core-lambda-stubs/android_common/meta_lic:dynamic' -d 'out/soong/.intermediates/system/libhidl/transport/base/1.0/android.hidl.base-V1.0-java/android_common/meta_lic:dynamic' -d 'out/soong/.intermediates/system/libhidl/transport/manager/1.0/android.hidl.manager-V1.0-java/android_common/meta_lic:dynamic' -s out/host/linux-x86/framework/android.test.base-hostdex.jar -s out/soong/.intermediates/build/soong/java/core-libraries/legacy.core.platform.api.stubs/android_common/dex/legacy.core.platform.api.stubs.jar -s out/soong/.intermediates/frameworks/base/android_stubs_current/android_common/dex/android_stubs_current.jar -s out/soong/.intermediates/frameworks/base/framework/android_common/combined/framework.jar -s out/soong/.intermediates/libcore/core-lambda-stubs/android_common/withres/core-lambda-stubs.jar -s out/target/product/barbet/system/framework/android.hidl.base-V1.0-java.jar -s out/target/product/barbet/system/framework/android.hidl.manager-V1.0-java.jar -s out/target/product/barbet/system/framework/android.test.base.jar -s out/target/product/barbet/system/framework/android.test.mock.jar -s out/target/product/barbet/system/framework/ext.jar -s out/target/product/barbet/system/framework/framework-res.apk -s out/target/product/barbet/system/framework/oat/arm/android.hidl.base-V1.0-java.odex -s out/target/product/barbet/system/framework/oat/arm/android.hidl.base-V1.0-java.vdex -s out/target/product/barbet/system/framework/oat/arm/android.hidl.manager-V1.0-java.odex -s out/target/product/barbet/system/framework/oat/arm/android.hidl.manager-V1.0-java.vdex -s out/target/product/barbet/system/framework/oat/arm/android.test.base.odex -s out/target/product/barbet/system/framework/oat/arm/android.test.base.vdex -s out/target/product/barbet/system/framework/oat/arm/android.test.mock.odex -s out/target/product/barbet/system/framework/oat/arm/android.test.mock.vdex -s out/target/product/barbet/system/framework/oat/arm/org.apache.http.legacy.odex -s out/target/product/barbet/system/framework/oat/arm/org.apache.http.legacy.vdex -s out/target/product/barbet/system/framework/oat/arm64/android.hidl.base-V1.0-java.odex -s out/target/product/barbet/system/framework/oat/arm64/android.hidl.base-V1.0-java.vdex -s out/target/product/barbet/system/framework/oat/arm64/android.hidl.manager-V1.0-java.odex -s out/target/product/barbet/system/framework/oat/arm64/android.hidl.manager-V1.0-java.vdex -s out/target/product/barbet/system/framework/oat/arm64/android.test.base.odex -s out/target/product/barbet/system/framework/oat/arm64/android.test.base.vdex -s out/target/product/barbet/system/framework/oat/arm64/android.test.mock.odex -s out/target/product/barbet/system/framework/oat/arm64/android.test.mock.vdex -s out/target/product/barbet/system/framework/oat/arm64/org.apache.http.legacy.odex -s out/target/product/barbet/system/framework/oat/arm64/org.apache.http.legacy.vdex -s out/target/product/barbet/system/framework/org.apache.http.legacy.jar -s out/target/product/barbet/system/framework/org.apache.http.legacy.jar.prof -t out/soong/.intermediates/development/samples/SystemUI/SystemUI/android_common/SystemUI.apk -i out/target/product/barbet/system/app/SystemUI/SystemUI.apk
使用 rule <rule-name>
,你可以定义一个用于重用的规则,命令格式如下:
m.<module>-<variant>-*
:其中 和 将在 Android.bp/Android.mk
中定义,而 * 可以视为不同的标志,如 javacFlags
或 kotlincFlags
。
build $ <function>
:其中 实际上是用于 1. 使用 aapt2
和 Java 编译器将 XML 文件和 Java 文件转换为 .class
文件,然后 2. 将 .class
文件转换为 .dex
文件以创建 .apk
文件的命令。
它还有其他规则,用于将所有生成的构件复制到相应的目录中。目录 out/target/product/bluejay/<obj>
用于分期“object”文件,这些中间二进制映像用于构建最终的程序。实际上落入目标文件系统的内容存储在 out/target/product/bluejay
目录下的 root、system 和 data 目录中。通常,它们被捆绑成称为 vbmeta.img、system.img、ramdisk.img 和 userdata.img
的镜像文件。
这些镜像的制作、打包和压缩也是由 Kati 生成的 ninja 文件的一部分。
rule m.microdroid_vbmeta_bootconfig_android_arm64_armv8-a.vbmeta
command = out/host/linux-x86/bin/avbtool make_vbmeta_image --key external/avb/test/data/testkey_rsa4096.pem --algorithm SHA256_RSA4096 --rollback_index $$(date -d 'TZ="GMT" 2022-09-05' +%s | head -1 | tr -d '$
') --rollback_index_location 0 --chain_partition bootconfig:1:out/soong/.intermediates/packages/modules/Virtualization/microdroid/microdroid_vbmeta_bootconfig/android_arm64_armv8-a/bootconfig.avbpubkey --chain_partition uboot_env:2:out/soong/.intermediates/packages/android_arm64_armv8-a/uboot_env.avbpubkey
--output out/soong/.intermediates/packages/modules/Virtualization/microdroid/microdroid_vbmeta_bootconfig/android_arm64_armv8-a/microdroid_vbmeta_bootconfig.img && truncate -s 65536 out/soong/.intermediates/packages/modules/Virtualization/microdroid/microdroid_vbmeta_bootconfig/android_arm64_armv8-a/microdroid_vbmeta_bootconfig.img # hash of input list: f8343f0c11db644e49c173205360d2628cff5f895d40de988e2f1dfac75e3524
结论
希望这篇文章让你对Android平台构建系统和AOSP有了较好的了解。
正如你可能注意到的,我在文章中多次提到了Bazel构建系统。它已经开始受到关注,有可能会取代当前的构建系统。我很愿意在未来的文章中更详细地谈论Bazel构建系统,随着事态的发展。