Understanding the Android 10.0 Compilation Process

Reading this article will take approximately 29 minutes.

Original content is not easy to create. If you find this useful, please feel free to share it or click the “Look” or “Share” button at the bottom right. Thank you!

[The Journey of Android] is based on the analysis of the source code of Android-Q (10.0)
The series of articles on “The Journey of Android”:
“System Startup”
The Journey of Android – Startup
Android System Architecture
How Android Starts
Android System Startup – init Process (1)
Android System Startup – init Process (2)
Android 10.0 System Startup – init Process (3)
Android 10.0 System Startup – init Process (4)
Android 10.0 System Startup – Zygote Process (1)
Android 10.0 System Startup – Zygote Process (2)
Android 10.0 System Startup – Zygote Process (3)
Android 10.0 System Startup – Zygote Process (4)
Android 10.0 System Startup – SystemServer Process (1)
Android 10.0 System Startup – SystemServer Process (2)
Android 10.0 System Services – AMS Startup Process
Android 10.0 System Startup – Launcher Startup Process
Android 10.0 Application Process Creation and Zygote Fork Process
Android 10.0 PackageManagerService (1) – Working Principle and Startup Process
Android 10.0 PackageManagerService (2) – Permission Scanning
Android 10.0 PackageManagerService (3) – APK Scanning
Android 10.0 PackageManagerService (4) – APK Installation Process
“Log System”
Android 10.0 Log System Analysis (1) – logd, logcat Command Description, Classification, and Properties
Android 10.0 Log System Analysis (2) – logd, logcat Architecture Analysis and Log System Initialization
Android 10.0 Log System Analysis (3) – logd, logcat Read and Write Log Source Code Analysis
Android 10.0 Log System Analysis (4) – selinux, Kernel Log Implementation in logd
“Binder Series”
Android 10.0 Binder Communication Principle (1) – Overview of Binder, HwBinder, VndBinder
Android 10.0 Binder Communication Principle (2) – Introduction to Binder
Android 10.0 Binder Communication Principle (3) – ServiceManager Part
Android 10.0 Binder Communication Principle (4) – Native-C/C++ Example Analysis
Android 10.0 Binder Communication Principle (5) – Binder Driver Analysis
Android 10.0 Binder Communication Principle (6) – How Binder Data Achieves Targeted Attacks
Android 10.0 Binder Communication Principle (7) – Framework Binder Example
Android 10.0 Binder Communication Principle (8) – Framework Layer Analysis
Android 10.0 Binder Communication Principle (9) – AIDL Binder Example
Android 10.0 Binder Communication Principle (11) – Binder Summary
“HwBinder Series”
HwBinder Introduction – Android 10.0 HwBinder Communication Principle (1)
HIDL Explained – Android 10.0 HwBinder Communication Principle (2)
HIDL Example – C++ Service Creation Client Verification – Android 10.0 HwBinder Communication Principle (3)
HIDL Example – JAVA Service Creation – Client Verification – Android 10.0 HwBinder Communication Principle (4)
HwServiceManager Part – Android 10.0 HwBinder Communication Principle (5)
Native Layer HIDL Service Registration Principle – Android 10.0 HwBinder Communication Principle (6)
Native Layer HIDL Service Acquisition Principle – Android 10.0 HwBinder Communication Principle (7)
JAVA Layer HIDL Service Registration Principle – Android 10.0 HwBinder Communication Principle (8)
JAVA Layer HIDL Service Acquisition Principle – Android 10.0 HwBinder Communication Principle (9)

“Compilation System Series”

Introduction to Compilation System – Android 10.0 Compilation System (1)

Compilation Environment Initialization – Android 10.0 Compilation System (2)

1 Overview

In the previous section, we clarified the process of initializing the compilation environment, the environment variables have been loaded, and the compilation target has been configured. Next, by executing a make command, we can proceed with the compilation.

What happens after make is executed is very interesting, let’s explore it together.

2 The Compilation History of the Android System

In Android 7.0, Google introduced the soong build system to gradually replace the GNU make compilation, so in Android 10.0, after executing make, we are using the soong build environment.

The compilation history of the Android system:

Understanding the Android 10.0 Compilation Process

3 Members of the Soong Compilation System Family

As can be seen from the diagram below, mk files are compiled into out/build-aosp_arm.ninja and out/build-aosp_arm-package.ninja, and bp files are compiled into out/soong/build.ninja. These three ninja files are then merged into out/combined-aosp_arm.ninja, and finally compiled through the ninja tool to complete the final compilation.

Understanding the Android 10.0 Compilation Process

4 The Flowchart of make

The flowchart of the soong build is shown in the diagram below:

Understanding the Android 10.0 Compilation Process

5 make()

After executing the make command, the make() function in envsetup.sh will be called for processing.

function make(){    _wrap_build $(get_make_command "$@") "$@"}

From get_make_command(), we can see that after make, the actual entry point for compilation is: build/soong/soong_ui.bash

function get_make_command(){    # If we're in the top of an Android tree, use soong_ui.bash instead of make    if [ -f build/soong/soong_ui.bash ]; then        # Always use the real make if -C is passed in        for arg in "$@"; do            if [[ $arg == -C* ]]; then                echo command make                return            fi        done        echo build/soong/soong_ui.bash --make-mode    else        echo command make    fi}

6 soong_ui.bash

6.1 The Call Stack of soong_ui.bash

Understanding the Android 10.0 Compilation Process

The execution process of soong_ui.bash:

  1. source microfactory.bash, obtain some function commands, for example: soong_build_go

  2. Compile /build/soong/cmd/soong_ui/main.go to generate the executable program out/soong_ui

  3. Execute the command: out/soong_ui –make-mode, executing the make command, will add “build/make/core/main.mk” to the build environment, while starting the kati, blueprint-soong, and ninja compilation.

Next, we will analyze the compilation process in detail according to the call stack.

6.2 [build/soong/soong_ui.bash]

soong_ui.bash is used to configure some resource environments and obtain some function commands, for example: soong_build_go, ultimately falling back to the root directory, executing out/soong_ui –make-mode for the actual build.

# Save the current PWD for use in soong_uiexport ORIGINAL_PWD=${PWD}export TOP=$(gettop)source ${TOP}/build/soong/scripts/microfactory.bashsoong_build_go soong_ui android/soong/cmd/soong_uicd ${TOP}exec "
$(getoutdir)/soong_ui" "$@"

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

Obtaining the function command of build_go and providing the function execution method of soong_build_go

[/build/soong/scripts/microfactory.bash]function soong_build_go{    BUILDDIR=$(getoutdir) \      SRCDIR=${TOP} \      BLUEPRINTDIR=${TOP}/build/blueprint \      EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong -pkg-path github.com/golang/protobuf=${TOP}/external/golang-protobuf" \      build_go $@}source ${TOP}/build/blueprint/microfactory/microfactory.bash

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

build_go primarily aims to build and generate the out/soong_ui executable program, used for the final compilation.

[/build/blueprint/microfactory/microfactory.bash ]function build_go{    # Increment when microfactory changes enough that it cannot rebuild itself.    # For example, if we use a new command line argument that doesn't work on older versions.    local mf_version=3    local mf_src="${BLUEPRINTDIR}/microfactory"    local mf_bin="${BUILDDIR}/microfactory_$(uname)"    local mf_version_file="${BUILDDIR}/.microfactory_$(uname)_version"    local built_bin="${BUILDDIR}/$1"    local from_src=1    if [ -f "${mf_bin}" ] && [ -f "${mf_version_file}" ]; then        if [ "${mf_version}" -eq "$(cat "${mf_version_file}")" ]; then            from_src=0        fi    fi    local mf_cmd    if [ $from_src -eq 1 ]; then        # `go run` requires a single main package, so create one        local gen_src_dir="${BUILDDIR}/.microfactory_$(uname)_intermediates/src"        mkdir -p "${gen_src_dir}"        sed "s/^package microfactory/package main/" "${mf_src}/microfactory.go" >"${gen_src_dir}/microfactory.go"        mf_cmd="${GOROOT}/bin/go run ${gen_src_dir}/microfactory.go"    else        mf_cmd="${mf_bin}"    fi    # GOROOT must be absolute because `go run` changes the local directory    GOROOT=$(cd $GOROOT; pwd) ${mf_cmd} -b "${mf_bin}" \            -pkg-path "github.com/google/blueprint=${BLUEPRINTDIR}" \            -trimpath "${SRCDIR}" \            ${EXTRA_ARGS} \            -o "${built_bin}" $2    if [ $? -eq 0 ] && [ $from_src -eq 1 ]; then        echo "${mf_version}" >"${mf_version_file}"    fi}

soong_ui ultimately compiles the command as follows:

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

From the above process, it can be seen that the generation of soong_ui involves several steps:

  • Compiling /build/blueprint/microfactory/microfactory.go to generate /out/microfactory_Linux

  • Using /out/microfactory_Linux to compile soong_ui

microfactory is a tool for incremental compilation of go programs. It is similar to “go install” but does not require GOPATH. The package-path mapping can be specified as a command-line option:

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

In fact, microfactory is a more advanced go command, it is compiled by go itself and replaces part of the go function, perfectly explaining the story of the chicken and the egg ^_^.

microfactory compilation example:

  • Prepare the go code

Create a directory hello in /home/ingresge/AP/AOSP_Q:

Create hello.go—vim hello/hello.go

Print “Hello, Go!” in it

package mainimport ("log" "os")func main() {  testlog := log.New(os.Stderr, "", log.Ltime)  testlog.Println("Hello, Go!")}
  • Use microfactory to compile 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/
  • Run

Execute the command: ./out/hellogo

Output result:

17:18:44 Hello, Go!

6.3 soong_ui

soong_ui is obtained by compiling build/soong/cmd/soong_ui/main.go, let’s analyze the process of main.go next.

6.3.1 The Call Stack of main.go

Understanding the Android 10.0 Compilation Process

6.3.2 main()

The entry point for starting the compilation of soong_ui

func main() {       var stdio terminal.StdioInterface       stdio = terminal.StdioImpl{}       // dumpvar uses stdout, everything else should be in stderr       if os.Args[1] == "--dumpvar-mode" || os.Args[1] == "--dumpvars-mode" {               stdio = terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)       }       writer := terminal.NewWriter(stdio)       defer writer.Finish()       log := logger.New(writer)       defer log.Cleanup()       if len(os.Args) < 2 || !(inList("--make-mode", os.Args) ||               os.Args[1] == "--dumpvars-mode" ||               os.Args[1] == "--dumpvar-mode") {               log.Fatalln("The `soong` native UI is not yet available.")       }       ctx, cancel := context.WithCancel(context.Background())       defer cancel()       trace := tracer.New(log)       defer trace.Close()       met := metrics.New()       stat := &status.Status{}       defer stat.Finish()       stat.AddOutput(terminal.NewStatusOutput(writer, os.Getenv("NINJA_STATUS"),               build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")))       stat.AddOutput(trace.StatusTracer())       build.SetupSignals(log, cancel, func() {               trace.Close()               log.Cleanup()               stat.Finish()       })       buildCtx := build.Context{ContextImpl: &build.ContextImpl{               Context: ctx,               Logger:  log,               Metrics: met,               Tracer:  trace,               Writer:  writer,               Status:  stat,       }}       var config build.Config       if os.Args[1] == "--dumpvars-mode" || os.Args[1] == "--dumpvar-mode" {               config = build.NewConfig(buildCtx)       } else {               config = build.NewConfig(buildCtx, os.Args[1:]...)       }       build.SetupOutDir(buildCtx, config)       logsDir := config.OutDir()       if config.Dist() {               logsDir = filepath.Join(config.DistDir(), "logs")       }       os.MkdirAll(logsDir, 0777)       log.SetOutput(filepath.Join(logsDir, "soong.log"))       trace.SetOutput(filepath.Join(logsDir, "build.trace"))       stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, "verbose.log")))       stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, "error.log")))       defer met.Dump(filepath.Join(logsDir, "build_metrics"))       if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {               if !strings.HasSuffix(start, "N") {                       if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {                               log.Verbosef("Took %dms to start up.",                                       time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())                               buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))                       }               }               if executable, err := os.Executable(); err == nil {                       trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))               }       }       // Fix up the source tree due to a repo bug where it doesn't remove       // linkfiles that have been removed       fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")       fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")       f := build.NewSourceFinder(buildCtx, config)       defer f.Shutdown()       build.FindSources(buildCtx, config, f)       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 {               if config.IsVerbose() {                       writer.Print("! The argument `showcommands` is no longer supported.")                       writer.Print("! Instead, the verbose log is always written to a compressed file in the output dir:")                       writer.Print("!")                       writer.Print(fmt.Sprintf("!   gzip -cd %s/verbose.log.gz | less -R", logsDir))                       writer.Print("!")                       writer.Print("! Older versions are saved in verbose.log.#.gz files")                       writer.Print("")                       time.Sleep(5 * time.Second)               }               toBuild := build.BuildAll               if config.Checkbuild() {                       toBuild |= build.RunBuildTests               }               build.Build(buildCtx, config, toBuild)       }}

The main execution flow of soong_ui is configured as follows:

1) runMakeProductConfig mainly configures the compilation parameters

2) runSoong compiles the tools, compiles blueprint and other compilation tools, compiles *.bp into out/soong/build.ninja

/.minibootstrap/build.ninja- Run minibp to generate .bootstrap/build.ninja (Primary stage) - Run minibp to generate .minibootstrap/build.ninja.in/.bootstrap/build.ninja- 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 loads build/make/core/main.mk, collects all Android.mk files and generates ninja files: out/build-aosp_arm.ninja

4) runKatiPackage loads build/make/packaging/main.mk, compiles and generates out/build-aosp_arm-package.ninja

5) createCombinedBuildNinjaFile merges out/soong/build.ninja, out/build-aosp_arm.ninja, and out/build-aosp_arm-package.ninja into out/combined-aosp_arm.ninja

6) runNinja runs the Ninja command, parses combined-aosp_arm.ninja, and executes the compilation process

The content of out/combined-aosp_arm.ninja is displayed as follows:

builddir = outpool local_pool depth = 42build _kati_always_build_: phonysubninja out/build-aosp_arm.ninjasubninja out/build-aosp_arm-package.ninjasubninja out/soong/build.ninja

6.3.4 Build

The entry point for Build

[/build/soong/ui/build/build.go]func Build(ctx Context, config Config, what int) {       ctx.Verboseln("Starting build with args:", config.Arguments())       ctx.Verboseln("Environment:", config.Environment().Environ())       if config.SkipMake() {               ctx.Verboseln("Skipping Make/Kati as requested")               what = what & (BuildSoong | BuildNinja)       }       if inList("help", config.Arguments()) {               help(ctx, config, what)               return       } else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {               clean(ctx, config, what)               return       }       // Make sure that no other Soong process is running with the same output directory       buildLock := BecomeSingletonOrFail(ctx, config)       defer buildLock.Unlock()       checkProblematicFiles(ctx)       SetupOutDir(ctx, config)       checkCaseSensitivity(ctx, config)       ensureEmptyDirectoriesExist(ctx, config.TempDir())       SetupPath(ctx, config)       if config.StartGoma() {               // Ensure start Goma compiler_proxy               startGoma(ctx, config)       }       if what&BuildProductConfig != 0 {               // Run make for product config               runMakeProductConfig(ctx, config)       }       if inList("installclean", config.Arguments()) {               installClean(ctx, config, what)               ctx.Println("Deleted images and staging directories.")               return       } else if inList("dataclean", config.Arguments()) {               dataClean(ctx, config, what)               ctx.Println("Deleted data files.")               return       }       if what&BuildSoong != 0 {               // Run Soong               runSoong(ctx, config)       }       if what&BuildKati != 0 {               // Run ckati               genKatiSuffix(ctx, config)               runKatiCleanSpec(ctx, config)               runKatiBuild(ctx, config)               runKatiPackage(ctx, config)               ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777)       } else {               // Load last Kati Suffix if it exists               if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {                       ctx.Verboseln("Loaded previous kati config:", string(katiSuffix))                       config.SetKatiSuffix(string(katiSuffix))               }       }       // Write combined ninja file       createCombinedBuildNinjaFile(ctx, config)       if what&RunBuildTests != 0 {               testForDanglingRules(ctx, config)       }       if what&BuildNinja != 0 {               if !config.SkipMake() {                       installCleanIfNecessary(ctx, config)               }               // Run ninja               runNinja(ctx, config)       }}

6.4 Analysis of main.mk File

During runKatiBuild, an important step is to load build/make/core/main.mk, which is the main control file of the Android Build system. Starting from main.mk, all necessary .mk files will be included through the include command, ultimately forming a collection of all compilation scripts in memory, which is equivalent to a huge Makefile file. The Makefile file appears very large, but it mainly consists of three types of content: variable definitions, function definitions, and target dependency rules. Additionally, the inclusion of mk files is also very important.

Understanding the Android 10.0 Compilation Process

Main.mk mainly does the following:

1. Define the compilation target product

2. Load config.mk to initialize related variables and detect the compilation environment and target environment

3. Clear rules, clearing dex files in the out directory

4. Load build/core/definitions.mk, which defines many general functions for use during the compilation process

5. Load the platform development toolkit build/make/core/pdk_config.mk

6. Load all Android.mk files in the system, which will ultimately be stored in out/.module_paths/Android.mk.list

7. Link type checking, verifying Link

8. The basic list of modules to be generated for this product is specified by the corresponding product definition file, defined in “product_config.mk”

9. Run APEX libraries and perform checks

10. All modules to be installed are stored in the variable ALL_DEFAULT_INSTALLED_MODULES, and the build/core/Makefile file is loaded in. The build/core/Makefile file generates the generation rules for images such as system.img, super.img, boot.img, and recovery.img based on the modules to be installed

11. Define the image target for compilation

12. Build the files and package them into rom format

6.4.1 Defining the Compilation Target Product

Process 1: When there is no KATI command, execute run_soong_ui, compiling via soong_ui.bash –make-mode

Process 2: When compiling via the kati command

In Android 10.0, the soong build is used, thus directly following process 2

The final compilation target is determined to be droid

ifndef KATIhost_prebuilts := linux-x86ifeq ($(shell uname),Darwin)host_prebuilts := darwin-x86endif# Process 1: When there is no KATI command, execute run_soong_ui, compiling via soong_ui.bash --make-mode.PHONY: run_soong_uirun_soong_ui:       +@prebuilts/build-tools/$(host_prebuilts)/bin/makeparallel --ninja build/soong/soong_ui.bash --make-mode $(MAKECMDGOALS).PHONY: $(MAKECMDGOALS)$(sort $(MAKECMDGOALS)) : run_soong_ui       @#emptyelse # KATI# Process 2: When compiling via the kati command, follow this process$(info [1/1] initializing build system ...)....#1. Define the compilation target product# This must be the first declared target..PHONY: droidDEFAULT_GOAL := droid$(DEFAULT_GOAL): droid_targets.PHONY: droid_targetsdroid_targets:...#endif # KATI

6.4.2 Loading config.mk

Load config.mk to initialize related variables, detect the compilation environment and target environment, and load clang/config.mk to configure some compilation environments.

include build/make/core/config.mk...# Load out/soong/make_vars-aosp_arm.mkinclude $(SOONG_MAKEVARS_MK)# Load some configurations for clang include $(BUILD_SYSTEM)/clang/config.mk...

6.4.3 Clearing Rules

Clearing rules, clearing dex files in the out directory.

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

6.4.4 Loading definitions.mk

Load build/core/definitions.mk, which defines many general functions for use during the compilation process

...include $(BUILD_SYSTEM)/definitions.mk# Load dex_preopt.mkinclude $(BUILD_SYSTEM)/dex_preopt.mkendif # KATI...

6.4.5 Loading pdk_config.mk

Load the platform development toolkit

...include build/make/core/pdk_config.mk# Generate dynamic linker warning for userdebug, eng, and non-REL versionsifneq ($(TARGET_BUILD_VARIANT),user)  ADDITIONAL_BUILD_PROPERTIES += ro.bionic.ld.warning=1else# As long as the user version is not the final version, enable it.ifneq ($(PLATFORM_VERSION_CODENAME),REL)  ADDITIONAL_BUILD_PROPERTIES += ro.bionic.ld.warning=1endifendif...

6.4.6 Loading all Android.mk in the system

Load all Android.mk files in the system, which will ultimately be stored in out/.module_paths/Android.mk.list.

If the environment variable ONE_SHOT_MAKEFILE is not empty, indicating that we are executing mm or mmm commands, it means that specific modules are to be compiled.

The paths of the Android.mk files for the specified modules to be compiled are stored in the environment variable ONE_SHOT_MAKEFILE, thus directly loading these Android.mk files obtains the corresponding compilation rules.

If the environment variable ONE_SHOT_MAKEFILE is empty and dont_bother is not true, all Android.mk file paths in the Android source code project will be obtained through out/soong/Android-aosp_arm.mk, and stored in out/.module_paths/Android.mk.list.

...ifneq ($(ONE_SHOT_MAKEFILE),)# We may have been called with the "mm" shell function with a subdirectory makefile. include $(SOONG_ANDROID_MK) $(wildcard $(ONE_SHOT_MAKEFILE))# Change CUSTOM_MODULES to include only modules that were# defined by this makefile; this will install all of those# modules as a side-effect.  Do this after including ONE_SHOT_MAKEFILE# so that the modules will be installed in the same place they# would have been with a normal make.CUSTOM_MODULES := $(sort $(call get-tagged-modules,$(ALL_MODULE_TAGS)))# Help target prints out the install pathdefine register_module_install_path.PHONY: GET-MODULE-INSTALL-PATH-$(1)GET-MODULE-INSTALL-PATH-$(1):       echo 'INSTALL-PATH: $(1) $(ALL_MODULES.$(1).INSTALLED)'endefSORTED_ALL_MODULES := $(sort $(ALL_MODULES))UNIQUE_ALL_MODULES :=$(foreach m,$(SORTED_ALL_MODULES),\\    $(if $(call streq,$(m),$(lastword $(UNIQUE_ALL_MODULES))),,\\        $(eval UNIQUE_ALL_MODULES += $(m))))SORTED_ALL_MODULES :=$(foreach mod,$(UNIQUE_ALL_MODULES),$(if $(ALL_MODULES.$(mod).INSTALLED),\    $(eval $(call register_module_install_path,$(mod)))\    $(foreach path,$(ALL_MODULES.$(mod).PATH),\        $(eval my_path_prefix := GET-INSTALL-PATH-IN)\        $(foreach component,$(subst /,$(space),$(path)),\            $(eval my_path_prefix := $$(my_path_prefix)-$$(component))\            $(eval .PHONY: $$(my_path_prefix))\            $(eval $$(my_path_prefix): GET-MODULE-INSTALL-PATH-$(mod))))))UNIQUE_ALL_MODULES :=else # ONE_SHOT_MAKEFILEifneq ($(dont_bother),true)FULL_BUILD := true# Include all makefiles in the system: out/.module_paths/Android.mk.listsubdir_makefiles := $(SOONG_ANDROID_MK) $(file <$(OUT_DIR)/.module_paths/Android.mk.list)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)))ifneq (,$(PDK_FUSION_PLATFORM_ZIP)$(PDK_FUSION_PLATFORM_DIR))# Load pdk_fusion_modules.mkinclude $(BUILD_SYSTEM)/pdk_fusion_modules.mkendif # PDK_FUSION_PLATFORM_ZIP || PDK_FUSION_PLATFORM_DIRdroid_targets : blueprint_toolsendif # dont_botherendif # ONE_SHOT_MAKEFILE...

6.4.7 Link Checking

Link type checking during compilation, verifying Link

...# Link type checking# "ALL_LINK_TYPES" contains a list of all link type prefixes (usually one for each module, but apk can "link" to java and native code).# Link type prefixes consist of all information required by the intermediates dir:##  LINK_TYPE:TARGET:_:2ND:STATIC_LIBRARIES:libfoo## All dependencies not listed in "allowed" or "warning" link types will become errorslink_type_error :=define link-type-prefix-base$(word 2,$(subst :,$(space),$(1)))endefdefine link-type-prefix$(if $(filter AUX-%,$(link-type-prefix-base)),$(patsubst AUX-%,AUX,$(link-type-prefix-base)),$(link-type-prefix-base))endefdefine link-type-aux-variant$(if $(filter AUX-%,$(link-type-prefix-base)),$(patsubst AUX-%,%,$(link-type-prefix-base)))endefdefine link-type-common$(patsubst _,,$(word 3,$(subst :,$(space),$(1))))endefdefine link-type-2ndarchprefix$(patsubst _,,$(word 4,$(subst :,$(space),$(1))))endefdefine link-type-class$(word 5,$(subst :,$(space),$(1)))endefdefine link-type-name$(word 6,$(subst :,$(space),$(1)))endefine link-type-os$(strip $(eval _p := $(link-type-prefix))\  $(if $(filter HOST HOST_CROSS,$(_p)),\    $($(_p)_OS),\    $(if $(filter AUX,$(_p)),AUX,android)))endefine link-type-arch$($(link-type-prefix)_$(link-type-2ndarchprefix)ARCH)endefine link-type-name-variant$(link-type-name) ($(link-type-class) $(link-type-os)-$(link-type-arch))endef...# Verify whether $(1) can link to $(2)# $(1) and $(2) are link type prefixes defined abovedefine verify-link-type$(foreach t,$($(2).TYPE),\  $(if $(filter-out $($(1).ALLOWED),$(t)),\    $(if $(filter $(t),$($(1).WARN)),\      $(call link-type-warning,$(1),$(2),$(t)),\      $(call link-type-error,$(1),$(2),$(t)))))endef# Verify that all branches/configurations have reasonable warnings/errors, and remove this rewriteverify-link-type = $(eval $$(1).MISSING := true)...

6.4.8 Loading product_config.mk

The basic list of modules to be generated for this product is specified by the corresponding product definition file, defined in “product_config.mk”

...# List most files installed for a specific product, including:# - PRODUCT_PACKAGES, and their LOCAL_REQUIRED_MODULES# - PRODUCT_COPY_FILES# The basic list of modules to be generated for this product is specified by the corresponding product definition file, defined in "product_config.mk"define product-installed-files  $(eval _mk := $(strip $(1))) \  $(eval _pif_modules := \    $(PRODUCTS.$(_mk).PRODUCT_PACKAGES) \    $(if $(filter eng,$(tags_to_install)),$(PRODUCTS.$(_mk).PRODUCT_PACKAGES_ENG)) \    $(if $(filter debug,$(tags_to_install)),$(PRODUCTS.$(_mk).PRODUCT_PACKAGES_DEBUG)) \    $(if $(filter tests,$(tags_to_install)),$(PRODUCTS.$(_mk).PRODUCT_PACKAGES_TESTS)) \    $(if $(filter asan,$(tags_to_install)),$(PRODUCTS.$(_mk).PRODUCT_PACKAGES_DEBUG_ASAN)) \    $(call auto-included-modules) \  ) \  $(eval ### Filter out the overridden packages and executables before doing expansion) \  $(eval _pif_overrides := $(call module-overrides,$(_pif_modules))) \  $(eval _pif_modules := $(filter-out $(_pif_overrides), $(_pif_modules))) \  $(eval ### Resolve the :32 :64 module name) \  $(eval _pif_modules_32 := $(patsubst %:32,%,$(filter %:32, $(_pif_modules)))) \  $(eval _pif_modules_64 := $(patsubst %:64,%,$(filter %:64, $(_pif_modules)))) \  $(eval _pif_modules_rest := $(filter-out %:32 %:64,$(_pif_modules))) \  $(eval ### Note for 32-bit product, 32 and 64 will be added as their original module names.) \  $(eval _pif_modules := $(call get-32-bit-modules-if-we-can, $(_pif_modules_32))) \  $(eval _pif_modules += $(_pif_modules_64)) \  $(eval ### For the rest we add both) \  $(eval _pif_modules += $(call get-32-bit-modules, $(_pif_modules_rest))) \  $(eval _pif_modules += $(call get-32-bit-modules, $(_pif_modules_rest))) \  $(call expand-required-modules,_pif_modules,$(_pif_modules),$(_pif_overrides)) \  $(filter-out $(HOST_OUT_ROOT)/%,$(call module-installed-files, $(_pif_modules))) \  $(call resolve-product-relative-paths,\    $(foreach cf,$(PRODUCTS.$(_mk).PRODUCT_COPY_FILES),$(call word-colon,2,$(cf))))endef...

6.4.9 Running APEX Libraries

Running APEX libraries and performing checks

...APEX_MODULE_LIBS := \  libadbconnection.so \  libadbconnectiond.so \  libandroidicu.so \  libandroidio.so \  libart-compiler.so \  libart-dexlayout.so \  libart-disassembler.so \  libart.so \  libartbase.so \  libartbased.so \  libartd-compiler.so \  libartd-dexlayout.so \  libartd.so \  libartpalette.so \  libc.so \  libdexfile.so \  libdexfile_external.so \  libdexfiled.so \  libdexfiled_external.so \  libdl.so \  libdt_fd_forward.so \  libdt_socket.so \  libicui18n.so \  libicuuc.so \  libjavacore.so \  libjdwp.so \  libm.so \  libnativebridge.so \  libnativehelper.so \  libnativeloader.so \  libnpt.so \  libopenjdk.so \  libopenjdkjvm.so \  libopenjdkjvmd.so \  libopenjdkjvmti.so \  libopenjdkjvmtid.so \  libpac.so \  libprofile.so \  libprofiled.so \  libsigchain.so \# Conscrypt APEX librariesAPEX_MODULE_LIBS += \  libjavacrypto.so \...# If the checks below fail, then some libraries have ended up in system/lib or system/lib64, whereas these libraries were only intended to be placed in some APEX packages.# The possible reason is that the libraries or binaries in /system have grown a direct or indirect dependency on a prohibited library.# To resolve this issue, locate the library's corresponding APEX package - search for it in the "native_shared_lib" property of the "APEX" build module (see e.g., art/build/APEX/android.bp).# Then check if the APEX package has the export library that should be used, i.e., the corresponding "cc_library" module with the "stubs" clause (e.g., libdexfile in art/libdexfile/android.bp).# If you cannot find the suitable APEX export library you need, or you think the libraries in /system should allow you to depend on them, please contact the owner of the APEX package.# If the exported libraries in APEX encounter this error, the APEX may be misconfigured, or an error occurred in the generation system. Please contact the APEX package owner and/or soong-team@, or [email protected] externally.  define check-apex-libs-absence    $(call maybe-print-list-and-error, \      $(filter $(foreach lib,$(APEX_MODULE_LIBS),%/$(lib)), \        $(filter-out $(foreach dir,$(APEX_LIBS_ABSENCE_CHECK_EXCLUDE), \                       $(TARGET_OUT)/$(if $(findstring %,$(dir)),$(dir),$(dir)/%)), \          $(filter $(TARGET_OUT)/lib/% $(TARGET_OUT)/lib64/%,$(1)))), \      APEX libraries found in system image (see comment for check-apex-libs-absence in \      build/make/core/main.mk for details))  endef# TODO(b/129006418): The check above catches libraries through product  # dependencies visible to make, but as long as they have install rules in  # /system they may still be created there through other make targets. To catch  # that we also do a check on disk just before the system image is built.  define check-apex-libs-absence-on-disk    $(hide) ( \      cd $(TARGET_OUT) &&& \      findres=$$(find lib* \        $(foreach dir,$(APEX_LIBS_ABSENCE_CHECK_EXCLUDE),-path "$(subst %,*,$(dir))" -prune -o) \        -type f \( -false $(foreach lib,$(APEX_MODULE_LIBS),-o -name $(lib)) \        -print) &&& \        if [ -n "$$findres" ]; then \          echo "APEX libraries found in system image (see comment for check-apex-libs-absence" 1>&2; \          echo "in build/make/core/main.mk for details):" 1>&2; \          echo "$$findres" | sort 1>&2; \          false; \        fi; \    )  endefendif...

6.4.10 Saving All Modules

All modules to be installed are stored in the variable ALL_DEFAULT_INSTALLED_MODULES, and the build/core/Makefile file is loaded.

... # The build/core/Makefile file generates the generation rules for images such as system.img, super.img, boot.img, and recovery.img based on the modules to be installed.# TODO: Remove the 3 places in the tree that use ALL_DEFAULT_INSTALLED_MODULES# and get rid of it from this list.modules_to_install := $(sort \    $(ALL_DEFAULT_INSTALLED_MODULES) \    $(product_target_FILES) \    $(product_host_FILES) \    $(call get-tagged-modules,$(tags_to_install)) \    $(CUSTOM_MODULES) \  )...# The build/make/core/Makefile contains additional content that we do not want to pollute this top-level Makefile.# It hopes that "ALL_DEFAULT_INSTALLED_MODULES" contains all modules built during the current make period, but it further expands "ALL_DEFAULT_INSTALLED_MODULES".ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install)include $(BUILD_SYSTEM)/Makefilemodules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES))ALL_DEFAULT_INSTALLED_MODULES :=...# This is used to obtain the correct order, you can also use these, but they are considered to have no files, so do not complain if their behavior changes.# Dependencies on all copied headers (see copy_headers.make). Other targets that require copying headers can depend on this target..PHONY: all_copied_headersall_copied_headers: ;$(ALL_C_CPP_ETC_OBJECTS): | all_copied_headers# All the droid stuff, in directories.PHONY: filesfiles: $(modules_to_install) \       $(INSTALLED_ANDROID_INFO_TXT_TARGET)...

6.4.11 Defining Image Targets for Compilation

The image targets during the compilation process are defined.

....PHONY: ramdiskramdisk: $(INSTALLED_RAMDISK_TARGET).PHONY: ramdisk_debugramdisk_debug: $(INSTALLED_DEBUG_RAMDISK_TARGET).PHONY: systemtarballsystemtarball: $(INSTALLED_SYSTEMTARBALL_TARGET).PHONY: boottarballboottarball: $(INSTALLED_BOOTTARBALL_TARGET).PHONY: userdataimageuserdataimage: $(INSTALLED_USERDATAIMAGE_TARGET)ifneq (,$(filter userdataimage, $(MAKECMDGOALS)))$(call dist-for-goals, userdataimage, $(BUILT_USERDATAIMAGE_TARGET))endif.PHONY: userdatatarballuserdatatarball: $(INSTALLED_USERDATATARBALL_TARGET).PHONY: cacheimagecacheimage: $(INSTALLED_CACHEIMAGE_TARGET).PHONY: bptimagebptimage: $(INSTALLED_BPTIMAGE_TARGET).PHONY: vendorimagevendorimage: $(INSTALLED_VENDORIMAGE_TARGET).PHONY: productimageproductimage: $(INSTALLED_PRODUCTIMAGE_TARGET).PHONY: productservicesimageproductservicesimage: $(INSTALLED_PRODUCT_SERVICES_IMAGE_TARGET).PHONY: odmimageodmimage: $(INSTALLED_ODMIMAGE_TARGET).PHONY: systemotherimagesystemotherimage: $(INSTALLED_SYSTEMOTHERIMAGE_TARGET).PHONY: superimage_emptysuperimage_empty: $(INSTALLED_SUPERIMAGE_EMPTY_TARGET).PHONY: bootimagebootimage: $(INSTALLED_BOOTIMAGE_TARGET).PHONY: bootimage_debugbootimage_debug: $(INSTALLED_DEBUG_BOOTIMAGE_TARGET).PHONY: vbmetaimagevbmetaimage: $(INSTALLED_VBMETAIMAGE_TARGET).PHONY: auxiliaryauxiliary: $(INSTALLED_AUX_TARGETS)...

6.4.12 Building the System and Packaging the ROM

Build the files and package them into ROM format.

....PHONY: droidcoredroidcore: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) \    $(INSTALLED_SYSTEMIMAGE_TARGET) \    $(INSTALLED_RAMDISK_TARGET) \    $(INSTALLED_BOOTIMAGE_TARGET) \    $(INSTALLED_DEBUG_RAMDISK_TARGET) \    $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \    $(INSTALLED_RECOVERYIMAGE_TARGET) \    $(INSTALLED_VBMETAIMAGE_TARGET) \    $(INSTALLED_USERDATAIMAGE_TARGET) \    $(INSTALLED_CACHEIMAGE_TARGET) \    $(INSTALLED_BPTIMAGE_TARGET) \    $(INSTALLED_VENDORIMAGE_TARGET) \    $(INSTALLED_ODMIMAGE_TARGET) \    $(INSTALLED_SUPERIMAGE_EMPTY_TARGET) \    $(INSTALLED_PRODUCTIMAGE_TARGET) \    $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) \    $(INSTALLED_FILES_FILE) \    $(INSTALLED_FILES_JSON) \    $(INSTALLED_FILES_FILE_VENDOR) \    $(INSTALLED_FILES_JSON_VENDOR) \    $(INSTALLED_FILES_FILE_ODM) \    $(INSTALLED_FILES_JSON_ODM) \    $(INSTALLED_FILES_FILE_PRODUCT) \    $(INSTALLED_FILES_JSON_PRODUCT) \    $(INSTALLED_FILES_FILE_PRODUCT_SERVICES) \    $(INSTALLED_FILES_JSON_PRODUCT_SERVICES) \    $(INSTALLED_FILES_FILE_SYSTEMOTHER) \    $(INSTALLED_FILES_JSON_SYSTEMOTHER) \    $(INSTALLED_FILES_FILE_RAMDISK) \    $(INSTALLED_FILES_JSON_RAMDISK) \    $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) \    $(INSTALLED_FILES_JSON_DEBUG_RAMDISK) \    $(INSTALLED_FILES_FILE_ROOT) \    $(INSTALLED_FILES_JSON_ROOT) \    $(INSTALLED_FILES_FILE_RECOVERY) \    $(INSTALLED_FILES_JSON_RECOVERY) \    $(INSTALLED_ANDROID_INFO_TXT_TARGET) \    auxiliary \    soong_docs...# Build a complete system - by default build droidcoredroid_targets: droidcore dist_files...

7 Summary

At this point, we have basically clarified the process of make, and later we will continue to analyze how ninja compiles build.ninja and how the image is packaged.

Leave a Comment

Your email address will not be published. Required fields are marked *