Android 编译系统(Build System)剖析

Android Build System剖析

Android预构建应用是如何制作的,背后的构建系统又是什么?

本文旨在分享关于Android构建系统以及与原始设备制造商(OEM)集成的知识,简化理解AOSP复杂机制的过程。与手动查阅各种文件及其内部工作流程相比,本文可以作为进入AOSP构建系统领域的快速指南。不涉及Gradle构建系统,Android平台构建系统与基于Gradle的Android应用构建系统截然不同,值得一提的是,大家是否曾想过Android预构建应用是如何制作的,背后的构建系统又是什么?对于大多数Android开发者来说,寻找关于Android操作系统核心和内部运行机制的优质资源是一个共同的痛点。Framework的开发并不容易,此外,构建一个带有其复杂性的Framework应用可能会令人困惑。

本文要点

在本文结尾,您将理解并了解有关Android平台构建系统的所有复杂性、功能、定义和关联。以下是四个详细讨论的要点:

  1. 环境设置
    • 运行 source build/envsetup.sh
  2. 选择要构建的目标产品
    • 运行 lunch <option>
  3. 构建代码
    • 运行 make <module-name>m <module-name>
  4. 在设备上运行/刷写/安装
    • 通过 adb push 推送apk
    • 通过 fastboot 刷写镜像

但在深入探讨以上4个步骤之前,有一些先决条件:

A. Android构建系统的演进

B. Android.bpblueprint

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_librarycontent_aar_plugin作为依赖项, 以生成GlanceApp.apk

bash 复制代码
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源码,请使用下面命令下载源码:

bash 复制代码
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)

该流程图说明了以下内容:

  1. Android.mk和其他Makefiles生成out/build-aosp_bluejay.ninja文件。
  2. 从Android.bp生成out/soong/build.ninja文件。此外,还会生成一个较小的out/combined-aosp_bluejay.ninja文件,负责将两者连接起来,作为执行入口。
  3. 最终,Ninja文件是真正直接控制源代码编译的工具,负责生成apk、aar和dex文件。APK的签名也是使用ninja规则完成的,然后完成这一切后,会创建*.imgs文件。

最后,让我们详细讨论这3个步骤。

  1. 运行source build/envsetup.sh
  2. 选择lunch选项
  3. 运行make <module-name>m <module-name>

Android编译的第一步

是运行source build/envsetup.sh

这为后续的编译步骤铺平了道路。了解envsetup.sh与我们的分析对应关系至关重要。

envsetup.sh脚本定义了许多函数。在执行此脚本后,您可以使用Linux命令在当前控制台直接执行这些函数,比如lunch、mm、mmm等(使用hmm查看所有可用命令)。因此,lunchmmm实际上是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_CHOICESAndroidProducts.mk.list。请查看以下代码段...

bash 复制代码
# 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)
  ...
}

不常见但是重要的命令

  1. set_stuff_for_environment:其功能是将一些路径添加到PATH环境变量中,以便我们可以在已执行lunch的控制台中使用Android源代码中的一些其他工具,例如模拟器。

  2. tapas:它为构建未捆绑的应用程序或APK(正常的Android包)设置构建环境。您可以选择芯片架构、构建变体以及模块构建所需的应用程序。例如:tapas SystemUI arm eng

  3. banchan:它为构建未捆绑的模块或APEX(本地服务、库、HAL等)设置构建环境。您可以选择芯片架构、构建变体以及模块构建所需的应用程序。例如:banchan com.android.boinic SystemUI arm eng

  4. make命令

如果您理解了lunch命令的原理,这些命令将变得非常容易。

  • m:从树的顶部进行编译。例如:mm <module-name>
  • mm:构建并安装当前目录中的所有模块及其依赖项。
  • mmm:构建并安装所提供目录中的所有模块及其依赖项。要限制构建的模块,可以使用以下语法:mmm dir/:target1,target2。例如:mmm packages/apps/Launcher3

还有其他几个支持的命令,可以使用hmm命令查看列表。

Android编译的第三步

makem

在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平台构建系统的核心。

bash 复制代码
# 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 mk2rbcrbcrun执行了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

go 复制代码
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/cleanhelp,它们调用cleanbuild.gohelp.shclobber/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

go 复制代码
# 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。然后继续进行:

go 复制代码
//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

go 复制代码
//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 文件。

go 复制代码
//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

bash 复制代码
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 为例。

bash 复制代码
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# 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 中定义,而 * 可以视为不同的标志,如 javacFlagskotlincFlags

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 文件的一部分。

bash 复制代码
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构建系统,随着事态的发展。

相关推荐
阿巴斯甜16 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker17 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952718 小时前
Andorid Google 登录接入文档
android
黄林晴19 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android