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

相关推荐
姑苏风2 小时前
《Kotlin实战》-附录
android·开发语言·kotlin
数据猎手小k5 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
你的小106 小时前
JavaWeb项目-----博客系统
android
风和先行7 小时前
adb 命令查看设备存储占用情况
android·adb
AaVictory.7 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
似霰8 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
大风起兮云飞扬丶8 小时前
Android——网络请求
android
干一行,爱一行8 小时前
android camera data -> surface 显示
android
断墨先生9 小时前
uniapp—android原生插件开发(3Android真机调试)
android·uni-app
无极程序员10 小时前
PHP常量
android·ide·android studio