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

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与Android.bp转成Ninja编译文件
该流程图说明了以下内容:

  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。请查看以下代码段…

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

# 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

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

# 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 中定义,而 * 可以视为不同的标志,如 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 文件的一部分。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/99676.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

jmeter做接口压力测试_jmeter接口性能测试

jmeter是apache公司基于java开发的一款开源压力测试工具&#xff0c;体积小&#xff0c;功能全&#xff0c;使用方便&#xff0c;是一个比较轻量级的测试工具&#xff0c;使用起来非常简单。因为jmeter是java开发的&#xff0c;所以运行的时候必须先要安装jdk才可以。jmeter是免…

3D开发工具HOOPS Publish如何快速创建交互式3D PDF文档?

HOOPS Publish是一款功能强大的SDK&#xff0c;可以创作丰富的工程数据并将模型文件导出为各种行业标准格式&#xff0c;包括PDF、STEP、JT和3MF。HOOPS Publish核心的3D数据模型是经过ISO认证的PRC格式(ISO 14739-1:2014)&#xff0c;它为装配树、拓扑和几何、产品制造信息和视…

【Cookie和Session的那些事儿】

&#x1f320;作者&#xff1a;TheMythWS. &#x1f386;专栏&#xff1a;《集合与数据结构》 &#x1f387;座右铭&#xff1a;不走心的努力都是在敷衍自己&#xff0c;让自己所做的选择&#xff0c;熠熠发光。 目录 认识Cookie和Session Cookie Cookie对象的特点 Cookie对…

学习 使用pandas库 DataFrame 使用

1 、 数据排序 sort_values()函数 by:要排序的名称或名称列表&#xff0c; sorted_df df.sort_values(byAge,ascendingFalse) 由大到小排序&#xff1b; sorted_df df.sort_values(byAge) 由小到大排序&#xff1b; # 创建一个示例数据帧 data {Name: [Tom, Nick, John…

【附安装包】Eplan2022安装教程

软件下载 软件&#xff1a;Eplan版本&#xff1a;2022语言&#xff1a;简体中文大小&#xff1a;1.52G安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU2.5GHz 内存4G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;https://pan.baidu.co…

ActiveMQ配置初探

文章目录 配置wrapper相关配置wrapper是干什么用的MQ的运行内存修改【需修改】修改内容题外话 wrapper.log配置【需修改】引起的问题优化方式 activemq.xml相关配置官网介绍配置管理后台的认证授权【建议修改】配置broker【根据自己需求更改】配置允许jmx监控关闭消息通知持久化…

ubuntu学习(五)----读取文件以及光标的移动

1、读取文件函数原型介绍 ssize_t read(int fd,void*buf,size_t count) 参数说明&#xff1a; fd: 是文件描述符 buf:为读出数据的缓冲区&#xff1b; count: 为每次读取的字节数&#xff08;是请求读取的字节数&#xff0c;读上来的数据保存在缓冲区buf中&#xff0c;同时文…

基于飞桨图学习框架的空间异配性感知图神经网络

本期文章将为大家分享飞桨社区开发者肖淙曦、周景博发表于数据挖掘顶会KDD2023的论文《Spatial Heterophily Aware Graph Neural Networks》。 肖淙曦 肖淙曦&#xff0c;百度研究院商业智能实验室研究实习生&#xff0c;中国科学技术大学在读博士生&#xff0c;主要从事时空…

CSC7203S 应用注意事项

CSC7203S 为高性能电流模式 PWM 开关电源功率转换器&#xff0c;满足绿色环保标准&#xff1b;广泛适用于经济型开关电源&#xff0c;如 DVD、机顶盒、传真机、打印机、LCD 显示器等。CSC7203S采用SOP-8封装。  内置 700V 高压功率开关管  输入电压&#xff08;85V~265V&a…

【Apollo学习笔记】——规划模块TASK之PATH_ASSESSMENT_DECIDER

文章目录 前言PATH_ASSESSMENT_DECIDER功能简介PATH_ASSESSMENT_DECIDER相关信息PATH_ASSESSMENT_DECIDER总体流程1. 去除无效路径2. 分析并加入重要信息给speed决策SetPathInfoSetPathPointType 3. 排序选择最优的路径4. 更新必要的信息 前言 在Apollo星火计划学习笔记——Ap…

时序预测 | MATLAB实现TCN-BiGRU时间卷积双向门控循环单元时间序列预测

时序预测 | MATLAB实现TCN-BiGRU时间卷积双向门控循环单元时间序列预测 目录 时序预测 | MATLAB实现TCN-BiGRU时间卷积双向门控循环单元时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.MATLAB实现TCN-BiGRU时间卷积双向门控循环单元时间序列预测&a…

SQL Server开启变更数据捕获(CDC)

一、CDC简介 变更数据捕获&#xff08;Change Data Capture &#xff0c;简称 CDC&#xff09;&#xff1a;记录 SQL Server 表的插入、更新和删除操作。开启cdc的源表在插入、更新和删除操作时会插入数据到日志表中。cdc通过捕获进程将变更数据捕获到变更表中&#xff0c;通过…

【校招VIP】产品面试之面试官的真实意图

考点介绍&#xff1a; 大厂面试时&#xff0c;面试官提出的问题除了了解经历和想法外&#xff0c;最看重的是思维逻辑能力、团队协作能力和协调能力。 『产品面试之面试官的真实意图』相关题目及解析内容可点击文章末尾链接查看&#xff01; 一、考点题目 1. 你遇到的最大的…

复杂性分析与算法设计:解锁计算机科学的奥秘

文章目录 算法复杂性分析的基本概念时间复杂度空间复杂度 常见的算法设计策略1. 分治法2. 贪心法3. 动态规划 算法设计的实际应用1. 网络路由2. 图像处理3. 人工智能 算法的选择和性能分析结论 &#x1f389;欢迎来到数据结构学习专栏~复杂性分析与算法设计&#xff1a;解锁计算…

linux中安装nodejs,卸载nodejs,更新nodejs,git,linux中安装nginx并配置

文章目录 node的安装与卸载&#xff08;更新版本&#xff09;卸载nodejs安装新版本node git安装与拉取代码安装解决 linux git 每次推拉(push/pull)代码都要输入用户名密码的问题 nginx 安装、配置和卸载安装nginx配置**.conf 文件内容 nginx 卸载 注意&#xff0c;我的是Ubunt…

buildAdmin的使用笔记

安装buildAdmin 下载完整包&#xff0c;解压进入 buildadmin 的文件夹&#xff0c; 输入命令 composer install 启动的时候使用&#xff0c; php think run 就可以了 为什么启动只需要&#xff0c; php think run 这种启动方式&#xff0c; 我是头一回看见 &#xff0c;后来才…

css-伪类:not实现列表最后一项没有样式

有了&#xff1a;not这个选择符&#xff0c;那么你将可以很好的处理类似这样的场景&#xff1a;假定有个列表&#xff0c;每个列表项都有一条底边线&#xff0c;但是最后一项不需要底边线。 示例&#xff1a; html: <ul><li>111111111111</li><li>21…

十二、集合(2)

本章概要 添加元素组集合的打印列表 List 添加元素组 在 java.util 包中的 Arrays 和 Collections 类中都有很多实用的方法&#xff0c;可以在一个 Collection 中添加一组元素。 Arrays.asList() 方法接受一个数组或是逗号分隔的元素列表&#xff08;使用可变参数&#xff…

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉农大图书馆

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉农大图书馆

百万级并发IM即时消息系统(3)配置数据初始化和前后端交互

04_配置数据初始化及前后端交互_哔哩哔哩_bilibili 1.配置文件 创建一个config文件夹以及一个app.yaml配置文件。 该文件专门存放一些关键配置&#xff0c;如mysql DNS路径和redis的addr账号密码等。 后期可以创建一个工具包和一些初始化方法&#xff0c;专门用来加载这些配…