当前位置:   article > 正文

make编译过程-Android10.0编译系统(三)_rk android make 流程分析

rk android make 流程分析

摘要:本节主要来进行Android10.0 编译系统的make过程

阅读本文大约需要花费29分钟。

文章首发微信公众号:IngresGe

专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢!

欢迎关注我的公众号!

[Android取经之路] 的源码都基于Android-Q(10.0) 进行分析

[Android取经之路] 系列文章:

《系统启动篇》

  1. Android系统架构
  2. Android是怎么启动的
  3. Android 10.0系统启动之init进程
  4. Android10.0系统启动之Zygote进程
  5. Android 10.0 系统启动之SystemServer进程
  6. Android 10.0 系统服务之ActivityMnagerService
  7. Android10.0系统启动之Launcher(桌面)启动流程
  8. Android10.0应用进程创建过程以及Zygote的fork流程
  9. Android 10.0 PackageManagerService(一)工作原理及启动流程
  10. Android 10.0 PackageManagerService(二)权限扫描
  11. Android 10.0 PackageManagerService(三)APK扫描
  12. Android 10.0 PackageManagerService(四)APK安装流程

《日志系统篇》

  1. Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性
  2. Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化
  3. Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析
  4. Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现​

《Binder通信原理》

  1. Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
  2. Android10.0 Binder通信原理(二)-Binder入门篇
  3. Android10.0 Binder通信原理(三)-ServiceManager篇
  4. Android10.0 Binder通信原理(四)-Native-C\C++实例分析
  5. Android10.0 Binder通信原理(五)-Binder驱动分析
  6. Android10.0 Binder通信原理(六)-Binder数据如何完成定向打击
  7. Android10.0 Binder通信原理(七)-Framework binder示例
  8. Android10.0 Binder通信原理(八)-Framework层分析
  9. Android10.0 Binder通信原理(九)-AIDL Binder示例
  10. Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub设计模式
  11. Android10.0 Binder通信原理(十一)-Binder总结

  《HwBinder通信原理》

  1. HwBinder入门篇-Android10.0 HwBinder通信原理(一)
  2.  HIDL详解-Android10.0 HwBinder通信原理(二)
  3. HIDL示例-C++服务创建Client验证-Android10.0 HwBinder通信原理(三)
  4. HIDL示例-JAVA服务创建-Client验证-Android10.0 HwBinder通信原理(四)
  5. HwServiceManager篇-Android10.0 HwBinder通信原理(五)
  6. Native层HIDL服务的注册原理-Android10.0 HwBinder通信原理(六)
  7. Native层HIDL服务的获取原理-Android10.0 HwBinder通信原理(七)
  8. JAVA层HIDL服务的注册原理-Android10.0 HwBinder通信原理(八)
  9. JAVA层HIDL服务的获取原理-Android10.0 HwBinder通信原理(九)
  10. HwBinder驱动篇-Android10.0 HwBinder通信原理(十)
  11. HwBinder原理总结-Android10.0 HwBinder通信原理(十一)

《编译原理》

  1. 编译系统入门篇-Android10.0编译系统(一)
  2. 编译环境初始化-Android10.0编译系统(二)
  3. make编译过程-Android10.0编译系统(三)
  4. Image打包流程-Android10.0编译系统(四)
  5. Kati详解-Android10.0编译系统(五)
  6. Blueprint简介-Android10.0编译系统(六)
  7. Blueprint代码详细分析-Android10.0编译系统(七)

 

1 概述

  上一节,我们理清了编译环境初始化的过程,环境变量已经加载,并配置了编译目标,接下来执行一个make命令我们就能够进行编译。

  make之后是怎么完成编译的,这个很有意思,我们一起往下探讨。

 

2 Android系统的编译历程

  Android7.0 Google引入了soong构建系统,用来逐步替代GNU make的编译,因此在Android10.0 上,make执行后,我们走的是soong构建环境。

  Android系统的编译历程:

 

3 Soong编译系统家族成员

 从下图可知,mk文件被编译成了 out/build-aosp_arm.ninja和out/build-aosp_arm-package.ninja,bp文件被编译成了out/soong/build.ninja,这三个ninja文件又被合并成out/combined-aosp_arm.ninja,最终通过ninja工具来编译out/combined-aosp_arm.ninja完成最终的编译。

 

4 make的流程图

  soong构建的流程图如下图所示:

 

5  make()

  执行完make命令后,会调用envsetup.sh的make()函数进行处理。

  1. function make()
  2. {
  3. _wrap_build $(get_make_command "$@") "$@"
  4. }

 从get_make_command()可以看出,make后,真正然后执行编译的入口是:build/soong/soong_ui.bash

  1. function get_make_command()
  2. {
  3. # If we're in the top of an Android tree, use soong_ui.bash instead of make
  4. if [ -f build/soong/soong_ui.bash ]; then
  5. # Always use the real make if -C is passed in
  6. for arg in "$@"; do
  7. if [[ $arg == -C* ]]; then
  8. echo command make
  9. return
  10. fi
  11. done
  12. echo build/soong/soong_ui.bash --make-mode
  13. else
  14. echo command make
  15. fi
  16. }

 

6 soong_ui.bash

6.1 soong_ui.bash调用栈

soong_ui.bash执行过程:

  1. source  microfactory.bash,得到一些函数命令, 例如:soong_build_go

  2. 编译/build/soong/cmd/soong_ui/main.go,生成 out/soong_ui这个可执行程序

  3. 执行命令:out/soong_ui --make-mode ,执行了make命令,会把"build/make/core/main.mk" 加到构建环境中,同时启动kati、blueprint-soong、ninja的编译。

接下来根据调用栈的流程,来详细分析编译的过程。

 

6.2 [build/soong/soong_ui.bash]

  soong_ui.bash 用来配置一些资源环境,得到一些函数命令,例如:soong_build_go,最终回退到根目录,执行out/soong_ui --make-mode进行真正的构建。

  1. # Save the current PWD for use in soong_ui
  2. export ORIGINAL_PWD=${PWD}
  3. export TOP=$(gettop)
  4. source ${TOP}/build/soong/scripts/microfactory.bash
  5. soong_build_go soong_ui android/soong/cmd/soong_ui
  6. cd ${TOP}
  7. exec "$(getoutdir)/soong_ui" "$@"

 

6.2.1 [/soong/../microfactory.bash]

  得到build_go的函数命令,并提供 soong_build_go的函数执行方法

  1. [/build/soong/scripts/microfactory.bash]
  2. function soong_build_go
  3. {
  4. BUILDDIR=$(getoutdir) \
  5. SRCDIR=${TOP} \
  6. BLUEPRINTDIR=${TOP}/build/blueprint \
  7. EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong -pkg-path github.com/golang/protobuf=${TOP}/external/golang-protobuf" \
  8. build_go $@
  9. }
  10. source ${TOP}/build/blueprint/microfactory/microfactory.bash

 

6.2.2 [/blueprint/../microfactory.bash]

  build_go主要目的就是用来构建生成 out/soong_ui这个可执行程序,用于参与最终的编译

  1. [/build/blueprint/microfactory/microfactory.bash ]
  2. function build_go
  3. {
  4. # Increment when microfactory changes enough that it cannot rebuild itself.
  5. # For example, if we use a new command line argument that doesn't work on older versions.
  6. local mf_version=3
  7. local mf_src="${BLUEPRINTDIR}/microfactory"
  8. local mf_bin="${BUILDDIR}/microfactory_$(uname)"
  9. local mf_version_file="${BUILDDIR}/.microfactory_$(uname)_version"
  10. local built_bin="${BUILDDIR}/$1"
  11. local from_src=1
  12. if [ -f "${mf_bin}" ] && [ -f "${mf_version_file}" ]; then
  13. if [ "${mf_version}" -eq "$(cat "${mf_version_file}")" ]; then
  14. from_src=0
  15. fi
  16. fi
  17. local mf_cmd
  18. if [ $from_src -eq 1 ]; then
  19. # `go run` requires a single main package, so create one
  20. local gen_src_dir="${BUILDDIR}/.microfactory_$(uname)_intermediates/src"
  21. mkdir -p "${gen_src_dir}"
  22. sed "s/^package microfactory/package main/" "${mf_src}/microfactory.go" >"${gen_src_dir}/microfactory.go"
  23. mf_cmd="${GOROOT}/bin/go run ${gen_src_dir}/microfactory.go"
  24. else
  25. mf_cmd="${mf_bin}"
  26. fi
  27. # GOROOT must be absolute because `go run` changes the local directory
  28. GOROOT=$(cd $GOROOT; pwd) ${mf_cmd} -b "${mf_bin}" \
  29. -pkg-path "github.com/google/blueprint=${BLUEPRINTDIR}" \
  30. -trimpath "${SRCDIR}" \
  31. ${EXTRA_ARGS} \
  32. -o "${built_bin}" $2
  33. if [ $? -eq 0 ] && [ $from_src -eq 1 ]; then
  34. echo "${mf_version}" >"${mf_version_file}"
  35. fi
  36. }

soong_ui最终的编译命令展开为:

  1. $(cd /prebuilts/go/linux-x86/; pwd) /out/microfactory_Linux
  2. -b "/out/microfactory_Linux" \
  3. -pkg-path "github.com/google/blueprint=/build/blueprint" \
  4. -trimpath "./" \
  5. -pkg-path android/soong=/build/soong
  6. -pkg-path github.com/golang/protobuf=/external/golang-protobuf} \
  7. -o "out/soong_ui" android/soong/cmd/soong_ui

从上面的流程可知,生成soong_ui经历几件事情:

  • 通过/build/blueprint/microfactory/microfactory.go 编译出/out/microfactory_Linux

  • 使用/out/microfactory_Linux来编译soong_ui

    microfactory是一个增量编译go程序的工具。它类似于“go install”,但不需要GOPATH。包->路径映射可以指定为命令行选项:

  1. -pkg-path android/soong=build/soong
  2. -pkg-path github.com/google/blueprint=build/blueprint

    其实microfactory就是一个高级一点的go命令,它自己由go编出来,又代替了一部分go的部分功能,鸡生蛋,蛋生鸡的故事,这里得到了完美解释 ^_^。

 

   microfactory编译示例:

  • 准备go的代码

        在/home/ingresge/AP/AOSP_Q中创建一个目录hello:

        创建hello.go---vim hello/hello.go

        在其中打印一个“Hello,Go!”

  1. package main
  2. import (
  3. "log"
  4. "os"
  5. )
  6. func main() {
  7. testlog := log.New(os.Stderr, "", log.Ltime)
  8. testlog.Println("Hello,Go!")
  9. }
  • 使用microfactory 编译hello.go
/home/ingresge/AP/AOSP_Q/out/microfactory_Linux -pkg-path android/hello=/home/ingresge/AP/AOSP_Q/hello -trimpath /home/ingresge/AP/AOSP_Q/hello -o /home/ingresge/AP/AOSP_Q/out/hellogo android/hello/
  • 运行

    执行命令:./out/hellogo

    输出结果:

17:18:44 Hello,Go!

 

6.3 soong_ui

  soong_ui 是通过编译 build/soong/cmd/soong_ui/main.go得来,我们接下来分析一下main.go的一些流程

 

6.3.1 main.go调用栈

 

6.3.2 main()

soong_ui启动编译的入口

  1. func main() {
  2. var stdio terminal.StdioInterface
  3. stdio = terminal.StdioImpl{}
  4. // dumpvar uses stdout, everything else should be in stderr
  5. if os.Args[1] == "--dumpvar-mode" || os.Args[1] == "--dumpvars-mode" {
  6. stdio = terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
  7. }
  8. writer := terminal.NewWriter(stdio)
  9. defer writer.Finish()
  10. log := logger.New(writer)
  11. defer log.Cleanup()
  12. if len(os.Args) < 2 || !(inList("--make-mode", os.Args) ||
  13. os.Args[1] == "--dumpvars-mode" ||
  14. os.Args[1] == "--dumpvar-mode") {
  15. log.Fatalln("The `soong` native UI is not yet available.")
  16. }
  17. ctx, cancel := context.WithCancel(context.Background())
  18. defer cancel()
  19. trace := tracer.New(log)
  20. defer trace.Close()
  21. met := metrics.New()
  22. stat := &status.Status{}
  23. defer stat.Finish()
  24. stat.AddOutput(terminal.NewStatusOutput(writer, os.Getenv("NINJA_STATUS"),
  25. build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")))
  26. stat.AddOutput(trace.StatusTracer())
  27. build.SetupSignals(log, cancel, func() {
  28. trace.Close()
  29. log.Cleanup()
  30. stat.Finish()
  31. })
  32. buildCtx := build.Context{ContextImpl: &build.ContextImpl{
  33. Context: ctx,
  34. Logger: log,
  35. Metrics: met,
  36. Tracer: trace,
  37. Writer: writer,
  38. Status: stat,
  39. }}
  40. var config build.Config
  41. if os.Args[1] == "--dumpvars-mode" || os.Args[1] == "--dumpvar-mode" {
  42. config = build.NewConfig(buildCtx)
  43. } else {
  44. config = build.NewConfig(buildCtx, os.Args[1:]...)
  45. }
  46. build.SetupOutDir(buildCtx, config)
  47. logsDir := config.OutDir()
  48. if config.Dist() {
  49. logsDir = filepath.Join(config.DistDir(), "logs")
  50. }
  51. os.MkdirAll(logsDir, 0777)
  52. log.SetOutput(filepath.Join(logsDir, "soong.log"))
  53. trace.SetOutput(filepath.Join(logsDir, "build.trace"))
  54. stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, "verbose.log")))
  55. stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, "error.log")))
  56. defer met.Dump(filepath.Join(logsDir, "build_metrics"))
  57. if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
  58. if !strings.HasSuffix(start, "N") {
  59. if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
  60. log.Verbosef("Took %dms to start up.",
  61. time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
  62. buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
  63. }
  64. }
  65. if executable, err := os.Executable(); err == nil {
  66. trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
  67. }
  68. }
  69. // Fix up the source tree due to a repo bug where it doesn't remove
  70. // linkfiles that have been removed
  71. fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
  72. fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
  73. f := build.NewSourceFinder(buildCtx, config)
  74. defer f.Shutdown()
  75. build.FindSources(buildCtx, config, f)
  76. if os.Args[1] == "--dumpvar-mode" {
  77. dumpVar(buildCtx, config, os.Args[2:])
  78. } else if os.Args[1] == "--dumpvars-mode" {
  79. dumpVars(buildCtx, config, os.Args[2:])
  80. } else {
  81. if config.IsVerbose() {
  82. writer.Print("! The argument `showcommands` is no longer supported.")
  83. writer.Print("! Instead, the verbose log is always written to a compressed file in the output dir:")
  84. writer.Print("!")
  85. writer.Print(fmt.Sprintf("! gzip -cd %s/verbose.log.gz | less -R", logsDir))
  86. writer.Print("!")
  87. writer.Print("! Older versions are saved in verbose.log.#.gz files")
  88. writer.Print("")
  89. time.Sleep(5 * time.Second)
  90. }
  91. toBuild := build.BuildAll
  92. if config.Checkbuild() {
  93. toBuild |= build.RunBuildTests
  94. }
  95. build.Build(buildCtx, config, toBuild)
  96. }
  97. }

    主要执行soong/ui/build/build.go,从build.go就可以看到执行soong的大体流程。

 main.go中配置的toBuild为 BuildProductConfig | BuildSoong | BuildKati | BuildNinja,支持productconfig\soong\kati\ninja的构建。

 

6.3.3 Build调用栈

编译步骤如下:

1) runMakeProductConfig 主要配置编译参数

2) runSoong 对工具进行编译,编译出blueprint等编译工具, 把*.bp 编译成 out/soong/build.ninja

  1. /.minibootstrap/build.ninja
  2. - Run minibp to generate .bootstrap/build.ninja (Primary stage) - Run minibp to generate .minibootstrap/build.ninja.in
  3. /.bootstrap/build.ninja
  4. - Build any bootstrap_go_binary rules and dependencies -- usually the primary builder and any build or runtime dependencies. - Run the primary builder to generate build.ninja

3) runKatiBuild, 加载 build/make/core/main.mk, 搜集所有的Android.mk文件生成ninja文件:out/build-aosp_arm.ninja

4) runKatiPackage, 加载build/make/packaging/main.mk, 编译生成out/build-aosp_arm-package.ninja

5) createCombinedBuildNinjaFile,将out/soong/build.ninja 、out/build-aosp_arm.ninja和out/build-aosp_arm-package.ninja, 合成为out/combined-aosp_arm.ninja

6) runNinja,运行Ninja命令, 解析combined-aosp_arm.ninja,执行编译过程

 

out/combined-aosp_arm.ninja 内容展示如下:

  1. builddir = out
  2. pool local_pool
  3. depth = 42
  4. build _kati_always_build_: phony
  5. subninja out/build-aosp_arm.ninja
  6. subninja out/build-aosp_arm-package.ninja
  7. subninja out/soong/build.ninja

 

6.3.4 Build

    Build入口

  1. [/build/soong/ui/build/build.go]
  2. func Build(ctx Context, config Config, what int) {
  3. ctx.Verboseln("Starting build with args:", config.Arguments())
  4. ctx.Verboseln("Environment:", config.Environment().Environ())
  5. if config.SkipMake() {
  6. ctx.Verboseln("Skipping Make/Kati as requested")
  7. what = what & (BuildSoong | BuildNinja)
  8. }
  9. if inList("help", config.Arguments()) {
  10. help(ctx, config, what)
  11. return
  12. } else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
  13. clean(ctx, config, what)
  14. return
  15. }
  16. // Make sure that no other Soong process is running with the same output directory
  17. buildLock := BecomeSingletonOrFail(ctx, config)
  18. defer buildLock.Unlock()
  19. checkProblematicFiles(ctx)
  20. SetupOutDir(ctx, config)
  21. checkCaseSensitivity(ctx, config)
  22. ensureEmptyDirectoriesExist(ctx, config.TempDir())
  23. SetupPath(ctx, config)
  24. if config.StartGoma() {
  25. // Ensure start Goma compiler_proxy
  26. startGoma(ctx, config)
  27. }
  28. if what&BuildProductConfig != 0 {
  29. // Run make for product config
  30. runMakeProductConfig(ctx, config)
  31. }
  32. if inList("installclean", config.Arguments()) {
  33. installClean(ctx, config, what)
  34. ctx.Println("Deleted images and staging directories.")
  35. return
  36. } else if inList("dataclean", config.Arguments()) {
  37. dataClean(ctx, config, what)
  38. ctx.Println("Deleted data files.")
  39. return
  40. }
  41. if what&BuildSoong != 0 {
  42. // Run Soong
  43. runSoong(ctx, config)
  44. }
  45. if what&BuildKati != 0 {
  46. // Run ckati
  47. genKatiSuffix(ctx, config)
  48. runKatiCleanSpec(ctx, config)
  49. runKatiBuild(ctx, config)
  50. runKatiPackage(ctx, config)
  51. ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777)
  52. } else {
  53. // Load last Kati Suffix if it exists
  54. if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
  55. ctx.Verboseln("Loaded previous kati config:", string(katiSuffix))
  56. config.SetKatiSuffix(string(katiSuffix))
  57. }
  58. }
  59. // Write combined ninja file
  60. createCombinedBuildNinjaFile(ctx, config)
  61. if what&RunBuildTests != 0 {
  62. testForDanglingRules(ctx, config)
  63. }
  64. if what&BuildNinja != 0 {
  65. if !config.SkipMake() {
  66. installCleanIfNecessary(ctx, config)
  67. }
  68. // Run ninja
  69. runNinja(ctx, config)
  70. }
  71. }

6.4 main.mk文件分析

  执行runKatiBuild时,有个重要的步骤,就是加载build/make/core/main.mk,main.mk文件是Android Build系统的主控文件。从main.mk开始,将通过include命令将其所有需要的.mk文件包含进来,最终在内存中形成一个包括所有编译脚本的集合,这个相当于一个巨大Makefile文件。Makefile文件看上去很庞大,其实主要由三种内容构成: 变量定义、函数定义和目标依赖规则,此外mk文件之间的包含也很重要。

  main.mk主要做了以下几件事情:

 1.定义编译目标product

 2.加载config.mk来初始化相关变量,检测编译环境和目标环境

 3.清除规则,清除out目录中的dex文件

 4.加载build/croe/definitions.mk,定义了很多通用函数,供编译过程调用

 5.加载平台开发工具包  build/make/core/pdk_config.mk

 6.加载系统中所有的Android.mk,最终会被存放到out/.module_paths/Android.mk.list

 7.Link 类型检查,校验Link

 8.要为此产品生成的模块的基本列表由相应的产品定义文件指定,这些定义在"product_config.mk"中

 9.运行时APEX库,并进行检查校验

 10. 将所有要安装的模块都保存在变量ALL_DEFAULT_INSTALLED_MODULES中,并且将build/core/Makefie文件加载进来。build/core/Makefie文件会根据要安装的模块生成system.img、super.img、boot.img和recovery.img等镜像文件的生成规则

 11.定义编译的image目标

 12.构建文件,然后将其打包成rom格式

 

6.4.1 定义编译目标product

  流程1:没有KATI命令时,走run_soong_ui执行,通过soong_ui.bash --make-mode 进行编译

  流程2:通过kati命令编译时

 

  在Android10.0中,使用的是soong构建,因此直接走流程2

  确定了最终的编译目标为droid

  1. ifndef KATI
  2. host_prebuilts := linux-x86
  3. ifeq ($(shell uname),Darwin)
  4. host_prebuilts := darwin-x86
  5. endif
  6. #流程1:没有KATI命令时,走run_soong_ui执行,通过soong_ui.bash --make-mode 进行编译
  7. .PHONY: run_soong_ui
  8. run_soong_ui:
  9. +@prebuilts/build-tools/$(host_prebuilts)/bin/makeparallel --ninja build/soong/soong_ui.bash --make-mode $(MAKECMDGOALS)
  10. .PHONY: $(MAKECMDGOALS)
  11. $(sort $(MAKECMDGOALS)) : run_soong_ui
  12. @#empty
  13. else # KATI
  14. #流程2:通过kati命令编译时,走该流程,Android10.0中走该流程
  15. $(info [1/1] initializing build system ...)
  16. ....
  17. #1.定义编译目标product
  18. #这是默认目标。它必须是第一个声明的目标。
  19. .PHONY: droid
  20. DEFAULT_GOAL := droid
  21. $(DEFAULT_GOAL): droid_targets
  22. .PHONY: droid_targets
  23. droid_targets:
  24. ...
  25. #endif #KATI

6.4.2 加载config.mk

   加载config.mk来初始化相关变量,检测编译环境和目标环境,加载clang/config.mk,配置一些编译的环境。

  1. include build/make/core/config.mk
  2. ...
  3. #加载out/soong/make_vars-aosp_arm.mk
  4. include $(SOONG_MAKEVARS_MK)
  5. #加载clang编译的一些配置
  6. include $(BUILD_SYSTEM)/clang/config.mk
  7. ...

6.4.3清除规则

  清除规则,清除out目录中的dex文件。

  1. ...
  2. .PHONY: clean-dex-files
  3. clean-dex-files:
  4. $(hide) find $(OUT_DIR) -name "*.dex" | xargs rm -f
  5. $(hide) for i in `find $(OUT_DIR) -name "*.jar" -o -name "*.apk"` ; do ((unzip -l $$i 2> /dev/null | \
  6. grep -q "\.dex$$" && rm -f $$i) || continue ) ; done
  7. @echo "All dex files and archives containing dex files have been removed."
  8. ...

6.4.4  加载definitions.mk

  加载build/croe/definitions.mk,定义了很多通用函数,供编译过程调用

  1. ...
  2. include $(BUILD_SYSTEM)/definitions.mk
  3. #加载dex_preopt.mk
  4. include $(BUILD_SYSTEM)/dex_preopt.mk
  5. endif # KATI
  6. ...

6.4.5 加载pdk_config.mk

  加载 平台开发工具包

  1. ...
  2. include build/make/core/pdk_config.mk
  3. #为userdebug、eng和non-REL生成启用动态链接器警告
  4. ifneq ($(TARGET_BUILD_VARIANT),user)
  5. ADDITIONAL_BUILD_PROPERTIES += ro.bionic.ld.warning=1
  6. else
  7. #只要user版本不是最终版本,就启用它。
  8. ifneq ($(PLATFORM_VERSION_CODENAME),REL)
  9. ADDITIONAL_BUILD_PROPERTIES += ro.bionic.ld.warning=1
  10. endif
  11. endif
  12. ...

6.4.6 加载系统所有的Android.mk

  加载系统中所有的Android.mk,最终会被存放到out/.module_paths/Android.mk.list。

  如果环境变量ONE_SHOT_MAKEFILE的值不等于空,也就是我们执行的是mm或者mmm命令,那么就表示要编译的是特定的模块。

 这些指定要编译的模块的Android.mk文件路径就保存在环境变量ONE_SHOT_MAKEFILE中,因此直接将这些Android,mk文件加载进来就获得相应的编译规则。

  如果环境变量ONE_SHOT_MAKEFILE的值等于空,且dont_bother不为true,会通过out/soong/Android-aosp_arm.mk 来获得Android源代码工程下的所有Android.mk文件的路径列表,并存入到out/.module_paths/Android.mk.list 中。

  1. ...
  2. ifneq ($(ONE_SHOT_MAKEFILE),)
  3. #我们可能已经被带有子目录makefile的“mm” shell函数调用了。
  4. include $(SOONG_ANDROID_MK) $(wildcard $(ONE_SHOT_MAKEFILE))
  5. # Change CUSTOM_MODULES to include only modules that were
  6. # defined by this makefile; this will install all of those
  7. # modules as a side-effect. Do this after including ONE_SHOT_MAKEFILE
  8. # so that the modules will be installed in the same place they
  9. # would have been with a normal make.
  10. CUSTOM_MODULES := $(sort $(call get-tagged-modules,$(ALL_MODULE_TAGS)))
  11. #帮助目标打印出安装路径
  12. define register_module_install_path
  13. .PHONY: GET-MODULE-INSTALL-PATH-$(1)
  14. GET-MODULE-INSTALL-PATH-$(1):
  15. echo 'INSTALL-PATH: $(1) $(ALL_MODULES.$(1).INSTALLED)'
  16. endef
  17. SORTED_ALL_MODULES := $(sort $(ALL_MODULES))
  18. UNIQUE_ALL_MODULES :=
  19. $(foreach m,$(SORTED_ALL_MODULES),\
  20. $(if $(call streq,$(m),$(lastword $(UNIQUE_ALL_MODULES))),,\
  21. $(eval UNIQUE_ALL_MODULES += $(m))))
  22. SORTED_ALL_MODULES :=
  23. $(foreach mod,$(UNIQUE_ALL_MODULES),$(if $(ALL_MODULES.$(mod).INSTALLED),\
  24. $(eval $(call register_module_install_path,$(mod)))\
  25. $(foreach path,$(ALL_MODULES.$(mod).PATH),\
  26. $(eval my_path_prefix := GET-INSTALL-PATH-IN)\
  27. $(foreach component,$(subst /,$(space),$(path)),\
  28. $(eval my_path_prefix := $$(my_path_prefix)-$$(component))\
  29. $(eval .PHONY: $$(my_path_prefix))\
  30. $(eval $$(my_path_prefix): GET-MODULE-INSTALL-PATH-$(mod))))))
  31. UNIQUE_ALL_MODULES :=
  32. else # ONE_SHOT_MAKEFILE
  33. ifneq ($(dont_bother),true)
  34. FULL_BUILD := true
  35. #包括系统中的所有makefile :out/.module_paths/Android.mk.list
  36. subdir_makefiles := $(SOONG_ANDROID_MK) $(file <$(OUT_DIR)/.module_paths/Android.mk.list)
  37. subdir_makefiles_total := $(words int $(subdir_makefiles) post finish)
  38. .KATI_READONLY := subdir_makefiles_total
  39. $(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk)))
  40. ifneq (,$(PDK_FUSION_PLATFORM_ZIP)$(PDK_FUSION_PLATFORM_DIR))
  41. # 加载pdk_fusion_modules.mk
  42. include $(BUILD_SYSTEM)/pdk_fusion_modules.mk
  43. endif # PDK_FUSION_PLATFORM_ZIP || PDK_FUSION_PLATFORM_DIR
  44. droid_targets : blueprint_tools
  45. endif # dont_bother
  46. endif # ONE_SHOT_MAKEFILE
  47. ...

6.4.7 Link检查

  编译时的Link 类型检查,校验Link

  1. ...
  2. #Link 类型检查
  3. #“ALL_LINK_TYPES”包含所有链接类型前缀的列表(通常每个模块一个,但是apk可以“链接”到java和本机代码)。
  4. #链接类型前缀由intermediates dir所需的所有信息组成:
  5. #
  6. # LINK_TYPE:TARGET:_:2ND:STATIC_LIBRARIES:libfoo
  7. #
  8. #所有未在“允许”或“警告”中列出的依赖关系链接类型都将成为错误
  9. link_type_error :=
  10. define link-type-prefix-base
  11. $(word 2,$(subst :,$(space),$(1)))
  12. endef
  13. define link-type-prefix
  14. $(if $(filter AUX-%,$(link-type-prefix-base)),$(patsubst AUX-%,AUX,$(link-type-prefix-base)),$(link-type-prefix-base))
  15. endef
  16. define link-type-aux-variant
  17. $(if $(filter AUX-%,$(link-type-prefix-base)),$(patsubst AUX-%,%,$(link-type-prefix-base)))
  18. endef
  19. define link-type-common
  20. $(patsubst _,,$(word 3,$(subst :,$(space),$(1))))
  21. endef
  22. define link-type-2ndarchprefix
  23. $(patsubst _,,$(word 4,$(subst :,$(space),$(1))))
  24. endef
  25. define link-type-class
  26. $(word 5,$(subst :,$(space),$(1)))
  27. endef
  28. define link-type-name
  29. $(word 6,$(subst :,$(space),$(1)))
  30. endef
  31. define link-type-os
  32. $(strip $(eval _p := $(link-type-prefix))\
  33. $(if $(filter HOST HOST_CROSS,$(_p)),\
  34. $($(_p)_OS),\
  35. $(if $(filter AUX,$(_p)),AUX,android)))
  36. endef
  37. define link-type-arch
  38. $($(link-type-prefix)_$(link-type-2ndarchprefix)ARCH)
  39. endef
  40. define link-type-name-variant
  41. $(link-type-name) ($(link-type-class) $(link-type-os)-$(link-type-arch))
  42. endef
  43. ...
  44. # 验证$(1)是否可以链接到$(2)
  45. # $(1)和$(2)都是上面定义的链接类型前缀
  46. define verify-link-type
  47. $(foreach t,$($(2).TYPE),\
  48. $(if $(filter-out $($(1).ALLOWED),$(t)),\
  49. $(if $(filter $(t),$($(1).WARN)),\
  50. $(call link-type-warning,$(1),$(2),$(t)),\
  51. $(call link-type-error,$(1),$(2),$(t)))))
  52. endef
  53. #验证所有分支/配置都有合理的警告/错误,并删除此重写
  54. verify-link-type = $(eval $$(1).MISSING := true)
  55. ...

6.4.8 加载product_config.mk

  要为此产品生成的模块的基本列表由相应的产品定义文件指定,这些定义在"product_config.mk"中

  1. ...
  2. #列出特定产品安装的大多数文件,包括:
  3. # - PRODUCT_PACKAGES, and their LOCAL_REQUIRED_MODULES
  4. # - PRODUCT_COPY_FILES
  5. # 要为此产品生成的模块的基本列表由相应的产品定义文件指定,这些定义在"product_config.mk"中
  6. define product-installed-files
  7. $(eval _mk := $(strip $(1))) \
  8. $(eval _pif_modules := \
  9. $(PRODUCTS.$(_mk).PRODUCT_PACKAGES) \
  10. $(if $(filter eng,$(tags_to_install)),$(PRODUCTS.$(_mk).PRODUCT_PACKAGES_ENG)) \
  11. $(if $(filter debug,$(tags_to_install)),$(PRODUCTS.$(_mk).PRODUCT_PACKAGES_DEBUG)) \
  12. $(if $(filter tests,$(tags_to_install)),$(PRODUCTS.$(_mk).PRODUCT_PACKAGES_TESTS)) \
  13. $(if $(filter asan,$(tags_to_install)),$(PRODUCTS.$(_mk).PRODUCT_PACKAGES_DEBUG_ASAN)) \
  14. $(call auto-included-modules) \
  15. ) \
  16. $(eval ### Filter out the overridden packages and executables before doing expansion) \
  17. $(eval _pif_overrides := $(call module-overrides,$(_pif_modules))) \
  18. $(eval _pif_modules := $(filter-out $(_pif_overrides), $(_pif_modules))) \
  19. $(eval ### Resolve the :32 :64 module name) \
  20. $(eval _pif_modules_32 := $(patsubst %:32,%,$(filter %:32, $(_pif_modules)))) \
  21. $(eval _pif_modules_64 := $(patsubst %:64,%,$(filter %:64, $(_pif_modules)))) \
  22. $(eval _pif_modules_rest := $(filter-out %:32 %:64,$(_pif_modules))) \
  23. $(eval ### Note for 32-bit product, 32 and 64 will be added as their original module names.) \
  24. $(eval _pif_modules := $(call get-32-bit-modules-if-we-can, $(_pif_modules_32))) \
  25. $(eval _pif_modules += $(_pif_modules_64)) \
  26. $(eval ### For the rest we add both) \
  27. $(eval _pif_modules += $(call get-32-bit-modules, $(_pif_modules_rest))) \
  28. $(eval _pif_modules += $(_pif_modules_rest)) \
  29. $(call expand-required-modules,_pif_modules,$(_pif_modules),$(_pif_overrides)) \
  30. $(filter-out $(HOST_OUT_ROOT)/%,$(call module-installed-files, $(_pif_modules))) \
  31. $(call resolve-product-relative-paths,\
  32. $(foreach cf,$(PRODUCTS.$(_mk).PRODUCT_COPY_FILES),$(call word-colon,2,$(cf))))
  33. endef
  34. ...

6.4.9 运行APEX库

  运行时APEX库,并进行检查校验

  1. ...
  2. APEX_MODULE_LIBS := \
  3. libadbconnection.so \
  4. libadbconnectiond.so \
  5. libandroidicu.so \
  6. libandroidio.so \
  7. libart-compiler.so \
  8. libart-dexlayout.so \
  9. libart-disassembler.so \
  10. libart.so \
  11. libartbase.so \
  12. libartbased.so \
  13. libartd-compiler.so \
  14. libartd-dexlayout.so \
  15. libartd.so \
  16. libartpalette.so \
  17. libc.so \
  18. libdexfile.so \
  19. libdexfile_external.so \
  20. libdexfiled.so \
  21. libdexfiled_external.so \
  22. libdl.so \
  23. libdt_fd_forward.so \
  24. libdt_socket.so \
  25. libicui18n.so \
  26. libicuuc.so \
  27. libjavacore.so \
  28. libjdwp.so \
  29. libm.so \
  30. libnativebridge.so \
  31. libnativehelper.so \
  32. libnativeloader.so \
  33. libnpt.so \
  34. libopenjdk.so \
  35. libopenjdkjvm.so \
  36. libopenjdkjvmd.so \
  37. libopenjdkjvmti.so \
  38. libopenjdkjvmtid.so \
  39. libpac.so \
  40. libprofile.so \
  41. libprofiled.so \
  42. libsigchain.so \
  43. # Conscrypt APEX libraries
  44. APEX_MODULE_LIBS += \
  45. libjavacrypto.so \
  46. ...
  47. #如果下面的检查失败,那么某些库已经在system/lib或system/lib64中结束,而这些库只打算放入一些APEX包中。
  48. #可能的原因是/system中的库或二进制文件已经增长了一个直接或间接拉入禁止的库的依赖关系。
  49. #要解决此问题,请查找库所属的APEX包-在“APEX”构建模块中的“native_shared_lib”属性中搜索它(参见art/build/APEX/安卓.bp例如)。
  50. #然后检查APEX包中是否有应该使用的导出库,即在其“native_shared_lib”属性中列出的库,对应的“cc_library”模块具有“stubs”子句(如art/libdexfile中的libdexfile_external/安卓.bp).
  51. #如果您找不到适合您需要的APEX导出库,或者您认为/system中应该允许您依赖的库,那么请与包含该库的APEX包的所有者联系。
  52. #如果在APEX中导出的库出现此错误,则APEX可能配置错误,或者生成系统中出现错误。
  53. #请联系APEX包主和/或soong-team@,或android-building@googlegroups.com外部的。
  54. define check-apex-libs-absence
  55. $(call maybe-print-list-and-error, \
  56. $(filter $(foreach lib,$(APEX_MODULE_LIBS),%/$(lib)), \
  57. $(filter-out $(foreach dir,$(APEX_LIBS_ABSENCE_CHECK_EXCLUDE), \
  58. $(TARGET_OUT)/$(if $(findstring %,$(dir)),$(dir),$(dir)/%)), \
  59. $(filter $(TARGET_OUT)/lib/% $(TARGET_OUT)/lib64/%,$(1)))), \
  60. APEX libraries found in system image (see comment for check-apex-libs-absence in \
  61. build/make/core/main.mk for details))
  62. endef
  63. # TODO(b/129006418): The check above catches libraries through product
  64. # dependencies visible to make, but as long as they have install rules in
  65. # /system they may still be created there through other make targets. To catch
  66. # that we also do a check on disk just before the system image is built.
  67. define check-apex-libs-absence-on-disk
  68. $(hide) ( \
  69. cd $(TARGET_OUT) && \
  70. findres=$$(find lib* \
  71. $(foreach dir,$(APEX_LIBS_ABSENCE_CHECK_EXCLUDE),-path "$(subst %,*,$(dir))" -prune -o) \
  72. -type f \( -false $(foreach lib,$(APEX_MODULE_LIBS),-o -name $(lib)) \) \
  73. -print) && \
  74. if [ -n "$$findres" ]; then \
  75. echo "APEX libraries found in system image (see comment for check-apex-libs-absence" 1>&2; \
  76. echo "in build/make/core/main.mk for details):" 1>&2; \
  77. echo "$$findres" | sort 1>&2; \
  78. false; \
  79. fi; \
  80. )
  81. endef
  82. endif
  83. ...

 

6.4.10 保存所有模块

  将所有要安装的模块都保存在变量ALL_DEFAULT_INSTALLED_MODULES中,并且将build/core/Makefie文件加载进来。

  1. ...
  2. #build/core/Makefie文件会根据要安装的模块生成system.img、super.img、boot.img和recovery.img等镜像文件的生成规则
  3. # TODO: Remove the 3 places in the tree that use ALL_DEFAULT_INSTALLED_MODULES
  4. # and get rid of it from this list.
  5. modules_to_install := $(sort \
  6. $(ALL_DEFAULT_INSTALLED_MODULES) \
  7. $(product_target_FILES) \
  8. $(product_host_FILES) \
  9. $(call get-tagged-modules,$(tags_to_install)) \
  10. $(CUSTOM_MODULES) \
  11. )
  12. ...
  13. #build/make/core/Makefile包含了我们不想污染这个顶级Makefile的额外内容。
  14. #它希望“ALL_DEFAULT_INSTALLED_MODULES”包含当前make期间构建的所有模块,但它还进一步扩展了“ALL_DEFAULT_INSTALLED_MODULES”。
  15. ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install)
  16. include $(BUILD_SYSTEM)/Makefile
  17. modules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES))
  18. ALL_DEFAULT_INSTALLED_MODULES :=
  19. ...
  20. #这是用来获得正确的秩序,你也可以使用这些,但他们被认为是没有文件,所以不要抱怨,如果他们的行为改变。
  21. #依赖于所有复制头的内部目标(请参见复制_标题.make). 需要首先复制头的其他目标可以依赖于此目标。
  22. .PHONY: all_copied_headers
  23. all_copied_headers: ;
  24. $(ALL_C_CPP_ETC_OBJECTS): | all_copied_headers
  25. # All the droid stuff, in directories
  26. .PHONY: files
  27. files: $(modules_to_install) \
  28. $(INSTALLED_ANDROID_INFO_TXT_TARGET)
  29. ...

 

6.4.11定义编译的image目标

  定义了我们编译过程中的所有image目标

  1. ...
  2. .PHONY: ramdisk
  3. ramdisk: $(INSTALLED_RAMDISK_TARGET)
  4. .PHONY: ramdisk_debug
  5. ramdisk_debug: $(INSTALLED_DEBUG_RAMDISK_TARGET)
  6. .PHONY: systemtarball
  7. systemtarball: $(INSTALLED_SYSTEMTARBALL_TARGET)
  8. .PHONY: boottarball
  9. boottarball: $(INSTALLED_BOOTTARBALL_TARGET)
  10. .PHONY: userdataimage
  11. userdataimage: $(INSTALLED_USERDATAIMAGE_TARGET)
  12. ifneq (,$(filter userdataimage, $(MAKECMDGOALS)))
  13. $(call dist-for-goals, userdataimage, $(BUILT_USERDATAIMAGE_TARGET))
  14. endif
  15. .PHONY: userdatatarball
  16. userdatatarball: $(INSTALLED_USERDATATARBALL_TARGET)
  17. .PHONY: cacheimage
  18. cacheimage: $(INSTALLED_CACHEIMAGE_TARGET)
  19. .PHONY: bptimage
  20. bptimage: $(INSTALLED_BPTIMAGE_TARGET)
  21. .PHONY: vendorimage
  22. vendorimage: $(INSTALLED_VENDORIMAGE_TARGET)
  23. .PHONY: productimage
  24. productimage: $(INSTALLED_PRODUCTIMAGE_TARGET)
  25. .PHONY: productservicesimage
  26. productservicesimage: $(INSTALLED_PRODUCT_SERVICESIMAGE_TARGET)
  27. .PHONY: odmimage
  28. odmimage: $(INSTALLED_ODMIMAGE_TARGET)
  29. .PHONY: systemotherimage
  30. systemotherimage: $(INSTALLED_SYSTEMOTHERIMAGE_TARGET)
  31. .PHONY: superimage_empty
  32. superimage_empty: $(INSTALLED_SUPERIMAGE_EMPTY_TARGET)
  33. .PHONY: bootimage
  34. bootimage: $(INSTALLED_BOOTIMAGE_TARGET)
  35. .PHONY: bootimage_debug
  36. bootimage_debug: $(INSTALLED_DEBUG_BOOTIMAGE_TARGET)
  37. .PHONY: vbmetaimage
  38. vbmetaimage: $(INSTALLED_VBMETAIMAGE_TARGET)
  39. .PHONY: auxiliary
  40. auxiliary: $(INSTALLED_AUX_TARGETS)
  41. ...

6.4.12 构建系统,打包rom

构建文件,然后将其打包成rom格式

  1. ...
  2. .PHONY: droidcore
  3. droidcore: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) \
  4. $(INSTALLED_SYSTEMIMAGE_TARGET) \
  5. $(INSTALLED_RAMDISK_TARGET) \
  6. $(INSTALLED_BOOTIMAGE_TARGET) \
  7. $(INSTALLED_DEBUG_RAMDISK_TARGET) \
  8. $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \
  9. $(INSTALLED_RECOVERYIMAGE_TARGET) \
  10. $(INSTALLED_VBMETAIMAGE_TARGET) \
  11. $(INSTALLED_USERDATAIMAGE_TARGET) \
  12. $(INSTALLED_CACHEIMAGE_TARGET) \
  13. $(INSTALLED_BPTIMAGE_TARGET) \
  14. $(INSTALLED_VENDORIMAGE_TARGET) \
  15. $(INSTALLED_ODMIMAGE_TARGET) \
  16. $(INSTALLED_SUPERIMAGE_EMPTY_TARGET) \
  17. $(INSTALLED_PRODUCTIMAGE_TARGET) \
  18. $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) \
  19. $(INSTALLED_FILES_FILE) \
  20. $(INSTALLED_FILES_JSON) \
  21. $(INSTALLED_FILES_FILE_VENDOR) \
  22. $(INSTALLED_FILES_JSON_VENDOR) \
  23. $(INSTALLED_FILES_FILE_ODM) \
  24. $(INSTALLED_FILES_JSON_ODM) \
  25. $(INSTALLED_FILES_FILE_PRODUCT) \
  26. $(INSTALLED_FILES_JSON_PRODUCT) \
  27. $(INSTALLED_FILES_FILE_PRODUCT_SERVICES) \
  28. $(INSTALLED_FILES_JSON_PRODUCT_SERVICES) \
  29. $(INSTALLED_FILES_FILE_SYSTEMOTHER) \
  30. $(INSTALLED_FILES_JSON_SYSTEMOTHER) \
  31. $(INSTALLED_FILES_FILE_RAMDISK) \
  32. $(INSTALLED_FILES_JSON_RAMDISK) \
  33. $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) \
  34. $(INSTALLED_FILES_JSON_DEBUG_RAMDISK) \
  35. $(INSTALLED_FILES_FILE_ROOT) \
  36. $(INSTALLED_FILES_JSON_ROOT) \
  37. $(INSTALLED_FILES_FILE_RECOVERY) \
  38. $(INSTALLED_FILES_JSON_RECOVERY) \
  39. $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
  40. auxiliary \
  41. soong_docs
  42. ...
  43. #构建一个完整的系统——默认情况下是构建droidcore
  44. droid_targets: droidcore dist_files
  45. ...

 

7 总结

  至此,make的流程我们基本理清了,后面还有ninja是如何把build.ninja编译出来的,image如何打包的,我们后面继续分析。

我的微信公众号:IngresGe

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/573122
推荐阅读
相关标签
  

闽ICP备14008679号