参考资料

https://blog.csdn.net/hansel/article/details/9841485

https://blog.csdn.net/wh_19910525/article/details/7519919

https://www.jianshu.com/p/fd86aef44365 (**,带图)

https://blog.csdn.net/qq_19923217/article/details/86711974 (**)

https://www.pianshen.com/article/7029777734/ (main.mk拓扑图)

https://blog.csdn.net/jscese/article/details/40897277

一、构建流程

(一)1.加载环境变量及方法

source build/envsetup.sh,envsetup.sh里会定义各种变量及函数,协助编译。

https://blog.csdn.net/gzzaigcnforever/article/details/20146483 (envsetup.mk中有很多实用的命令字)

1-1)函数

lunch:选择产品
croot()                 # 回到根目录
jgrep()                 # 查找java文件
cgrep()                 # 查找c/cpp文件
ggrep就是搜索.gradle文件
jgrep就是搜索.java文件
resgrep就是搜索.xml文件
mangrep就是搜索AndroidManifest.xml文件
sepgrep就是搜索sepolicy目录下的文件
rcgrep就是搜索.rc文件
mgrep就是搜索makefile文件
treegrep就是搜索c|h|cpp|hpp|S|java|xml文件

1-2)变量

PRODUCT_XX
OUT_DIR:即out目录

TARGET系列变量

build/make/core/envsetup.mk:223:TARGET_COPY_OUT_SYSTEM := system
build/make/core/envsetup.mk:436:TARGET_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM)
build/make/core/envsetup.mk:311:PRODUCT_OUT := $(TARGET_PRODUCT_OUT_ROOT)/$(TARGET_DEVICE)
build/make/core/envsetup.mk:306:TARGET_PRODUCT_OUT_ROOT := $(TARGET_OUT_ROOT)/product

SOONG系列变量

SOONG_HOST_OUT_EXECUTABLES := $(SOONG_HOST_OUT)/bin
SOONG_HOST_OUT := $(SOONG_OUT_DIR)/host/$(HOST_OS)-$(HOST_PREBUILT_ARCH)
SOONG_OUT_DIR := $(OUT_DIR)/soong

HOST系列变量

build/make/core/envsetup.mk:105: HOST_OS := linux
build/make/core/envsetup.mk:216:HOST_PREBUILT_ARCH := x86
build/make/core/envsetup.mk:294:HOST_OUT_ROOT := $(OUT_DIR)/host
build/make/core/envsetup.mk:299:HOST_OUT := $(HOST_OUT_ROOT)/$(HOST_OS)-$(HOST_PREBUILT_ARCH)
build/make/core/envsetup.mk:325:HOST_OUT_EXECUTABLES := $(HOST_OUT)/bin

(一)2.配置产品 (产品定制)

https://www.cnblogs.com/caseyzq/p/17124317.html
https://blog.csdn.net/u014674293/article/details/105158562/
https://www.modb.pro/db/616099 (**)
https://www.jianshu.com/p/d408314b2ca4

(一)2.1、产品定制的核心文件

对于一个产品的定义通常至少会包括四个文件:

(一)2.1.1.AndroidProducts.mk

定义了PRODUCT_MAKEFILES与COMMON_LUNCH_CHOICES两个变量

(一)2.1.2.产品版本定义文件

该文件可能不止一个,因为同一个产品可能会有多种版本。通常情况下我们并不需要定义所有这些变量。Build 系统的已经预先定义好了一些组合,它们都位于 /build/target/product 下,每个文件定义了一个组合,我们只要集成这些预置的定义,然后再覆盖自己想要的变量定义即可。

# Overrides
PRODUCT_NAME := full
PRODUCT_DEVICE := generic
PRODUCT_BRAND := Android
PRODUCT_MODEL := AOSP on ARM Emulator

一般项目不直接去修改产品版本对应的mk文件,而是在vendor目录中自定义一个mk文件,在产品版本对应的mk文件中调用inherit-product函数继承自定义的mk文件。如
device/qcom/qssi/qssi.mk:281:$(call inherit-product, vendor/mgy/mgy.mk)

(一)2.1.3.BoardConfig.mk

该文件用来配置硬件主板,它其中定义的都是设备底层的硬件特性。例如:该设备的主板相关信息,WiFi 相关信息,还有 BootLoader,内核,radioimage 等信息。

3-1)BoardConfig.mk的调用逻辑:

build/make/core/main.mk:49:include build/make/core/config.mk

build/make/core/config.mk:230:include $(BUILD_SYSTEM)/envsetup.mk

build/make/core/envsetup.mk:280:include $(BUILD_SYSTEM)/board_config.mk

build/make/core/board_config.mk:116: board_config_mk := $(TARGET_DEVICE_DIR)/BoardConfig.mk

3-2)BoardConfig.mk中重要的变量
最新的 Android build 具有板级配置,用于控制将哪些模块复制到每个阶段,以及加载哪些模块。本部分重点介绍以下子集:

BOARD_VENDOR_RAMDISK_KERNEL_MODULES:要复制到 ramdisk 的模块列表。
BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD:要在第一阶段 init 中加载的模块列表。
BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD:从 ramdisk 中选择 recovery 或 fastbootd 时要加载的模块列表。
BOARD_VENDOR_KERNEL_MODULES:要复制到 vendor 或 vendor_dlkm 分区中的 /vendor/lib/modules/ 目录下的模块列表。
BOARD_VENDOR_KERNEL_MODULES_LOAD:要在第二阶段 init 中加载的模块列表。
(一)2.1.4.vendorsetup.sh

(一)2.2、产品定制常用重要变量

PRODUCT_COPY_FILES为何不能在Android.mk中使用:
https://blog.csdn.net/gzzaigcnforever/article/details/51182118

  • PRODUCT_PROPERTY_OVERRIDES
    作用:添加自定义系统属性,或者复写系统属性。
    解析位置:main.mk文件(有的版本可能分散在include的mk文件中)

https://0xforee.top/2016/03/29/android-build-system-parse-PRODUCT_PROPERTY_OVERRIDES/

(一)2.3、lunch (选择产品)

lunch <product_name>-<build_variant>
之后会打印配置结果:

lunch之后会给一些全局赋值,这些变量将会影响到整个编译过程。

PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=10
TARGET_PRODUCT=kona
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm64
TARGET_ARCH_VARIANT=armv8-a
TARGET_CPU_VARIANT=kryo300
TARGET_2ND_ARCH=arm
TARGET_2ND_ARCH_VARIANT=armv8-2a
TARGET_2ND_CPU_VARIANT=cortex-a75
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-5.15.0-92-generic-x86_64-Ubuntu-20.04.5-LTS
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=QKQ1.210702.001
OUT_DIR=out

产品列表是如何加载出来的

https://www.cnblogs.com/caseyzq/p/17124317.html 作了详细说明。
以Android10为例:
见print_lunch_menu函数,通过调用get_build_var函数来获取编译变量COMMON_LUNCH_CHOICES.
新的Android版本,通过在AndroidProducts.mk中定义COMMON_LUNCH_CHOICES

AndroidProducts.mk如何被加载

1)当用户执行lunch命令时会调用get_build_var,build_build_var_cache等shell函数
2)这些shell函数又会调用build/soong/soong_ui.bash --dumpvar-mode
3)soong_ui.bash走到/build/soong/cmd/soong_ui/main.go中的main函数中
4)main函数中调用build.FindSources(buildCtx, config, f)
在device、vendor、product目录中查找AndroidProducts.mk文件。
并将所有的名为AndroidProducts.mk文件路径记录在AndroidProducts.mk.list

AndroidProducts.mk.list调用关系

AndroidProducts.mk.list会在makefile宏函数_find-android-products-files中被调用
$(_find-android-products-files)被get-all-product-makefiles调用
最终结果:
函数返回所有AndroidProducts.mk中定义的PRODUCT_MAKEFILES的值(全都囊括)
更新COMMON_LUNCH_CHOICES变量的值,将所有AndroidProducts.mk中定义的COMMON_LUNCH_CHOICES的值都囊括

AndroidProducts.mk.list是没有包括./build/make/target/product/AndroidProducts.mk的

choices并没有全部包含AndroidProducts.mk的choices

(一)3.编译指定的产品

通过上一步lunch指定了产品信息之后,shell已经存储了一些关于产品的变量如TARGET_PRODUCT,make时会根据TARGET_PRODUCT变量又生成新的配置。

二、build/core下的各个mk文件详解

(0)、Makefile分类

https://blog.csdn.net/nei504293736/article/details/90175567
整个Android Build系统中的 Make 文件可以分为三类:

1.Build系统核心 Makefile
这类makefile定义了整个Build系统的框架,而其他所有Make文件都是在这个框架的基础上编写出来的。位于/build/core目录下。
2.针对某个产品的Makefile
这类makefile是针对某个产品Make文件,这些文件通常位于device/<vendor>/<product>目录下。
3.针对某个模块的Makefile Android.mk
第三类是针对某个模块的makefile文件.AOSP中,不会针对某一个文件进行编译,每一个编译单位都是一个模块,每一个模块由一个名为"Android.mk"的makefile来声明。该文件中定义了如何编译当前模块。

各个makefile的功能:

(一)、main.mk (根mk)

https://blog.csdn.net/kris_fei/article/details/77619974
https://www.cnblogs.com/Oude/p/12553545.html

(一)-1.mk依赖图谱

除了以上的mk文件,main.mk还会include同级的Makefile,main.mk还会遍历各个模块下的Android.mk并include,不包括out,repo和git目录。见https://blog.csdn.net/weixin_30558777/article/details/117635191

 493 #
 494 # Include all of the makefiles in the system
 495 #
 496 
 497 subdir_makefiles := $(SOONG_ANDROID_MK) $(file <$(OUT_DIR)/.module_paths/Android.mk.list)
 498 subdir_makefiles_total := $(words int $(subdir_makefiles) post finish)
 499 .KATI_READONLY := subdir_makefiles_total
 500 
 501 $(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk     )))

(一)-2.重要的变量整理

  • BUILD_SYSTEM
    BUILD_SYSTEM := $(TOPDIR)build/core,会include很多其下的mk文件及Makefile文件,如include $(BUILD_SYSTEM)/config.mk

  • FILE_NAME_TAG := eng.$(BUILD_USERNAME)
    BUILD_USERNAME为用户名,FILE_NAME_TAG在后面生成target包时会用到。

系统源码make的时候,默认执行main.mk里的droidcore伪目录,生成各种镜像,并通过dist_files伪目录将镜像文件拷贝至out/dist目录。

(二)、envsetup.mk

http://bcoder.com/java/summary-of-constant-values-in-android-mk-compile-system

include的mk文件

1.include $(BUILD_SYSTEM)/product_config.mk

2.*/BoardConfig.mk (板级配置)
注意这个文件是根据配置来include的,并不是写死的,以rk3399为例:

有两个变量SRC_TARGET_DIR与TARGET_DEVICE,其中SRC_TARGET_DIR变量定义在config.mk文件中,为build/target;

-----》TARGET_DEVICE在哪里定义的?《-----
见后面对product_config.mk的讲解,为rk3399_box,所以加载的BoardConfig.mk文件为:device/rockchip/rk3399/rk3399_box/BoardConfig.mk

(三)、definitions.mk

该文件定义了在编译过程需要调用到的各种自定义函数。

  • intermediates-dir-for
    https://blog.csdn.net/wanshilun/article/details/77852486
    根据参数结合模板返回指定目录,如传入的参数:$1= PACKAGING , $2=target-files.$3及以后的参数为空,产品名为qssi则systemimage_intermediates := $(call intermediates-dir-for,PACKAGING,systemimage),systemimage_intermediates最终为out/target/product/qssi/obj/PACKAGING/systemimage_intermediates

  • copy-file-to-target
    拷贝

    2629 define copy-file-to-target
    2630 @mkdir -p $(dir $@)
    2631 $(hide) rm -f $@
    2632 $(hide) cp "$<" "$@"
    2633 endef

比如镜像生成过程中,会创建/out/target/product/xxx 目录, xxx表示产品的名称,然后把文件拷贝到该目录中。

(四)、base_rules.mk

https://www.it610.com/article/5088835.htm
https://blog.csdn.net/sunjianguang90/article/details/50427747
base_rules.mk里定义了生成某种目标的方式
目标类型:主机上的可执行程序,设备上的可执行程序,apk程序,Java运行库,动态链接库等等
Android编译系统里每一种目标的生成方式对应一个makefile,
示例:如果某个模块需要编译成手机上的二进制程序,它需要include $(BUILD_EXECUTABLE)
而BUILD_EXECUTABLE指向的是$(BUILD_SYSTEM)/executable.mk
所有生成方式对应的makefile都将包含base_rules.mk

  • 各种LOCAL_XXX开头的全局变量就定义在此

  • clear_vars.mk
    清空除了LOCAL_PATH的所有LOCAL_XXX开头的全局变量

  • defination.mk
    https://www.jianshu.com/p/aaf44b513c63

  • 获取特定类型的文件
    美元符号调用的宏都定义在这里,如

    
    LOCAL_PATH := $(call my-dir)中的my-dir

include $(call all-makefiles-under,$(LOCAL_PATH))中的 all-makefiles-under。

$(call my-dir):获取当前文件夹路径。
$(call all-java-files-under, <src>):获取指定目录下的所有 Java 文件。
$(call all-c-files-under, <src>):获取指定目录下的所有 C 语言文件。
$(call all-Iaidl-files-under, <src>) :获取指定目录下的所有 AIDL 文件。
$(call all-makefiles-under, <folder>):获取指定目录下的所有 Make 文件。
$(call intermediates-dir-for, <class>, <app_name>, <host or target>, <common?> ):获取 Build 输出的目标文件夹路径。


### (五)、config.mk
include $(BUILD_SYSTEM)/envsetup.mk
include $(BUILD_SYSTEM)/dumpvar.mk

> **初始化变量**

1)各种BUILD_XXX_LIBRARY的变量都定义在这个文件里,常用编译目标:
```Shell
BUILD_HOST_STATIC_LIBRARY   主机上的静态库
BUILD_HOST_SHARED_LIBRARY   主机上的动态库
BUILD_HOST_EXECUTABLE   主机上的可执行文件
BUILD_STATIC_LIBRARY    目标设备上的静态库
BUILD_SHARED_LIBRARY    目标设备上的动态库
BUILD_EXECUTABLE    目标设备上的可执行文件
BUILD_JAVA_LIBRARY  JAVA库
BUILD_STATIC_JAVA_LIBRARY   静态JAVA库
BUILD_HOST_JAVA_LIBRARY 主机上的JAVA库
BUILD_PACKAGE   APK程序

2)SRC_XXX变量

  • xxx_library.mk
    这里定义的就是生成各种xxx库的具体的生成规则

  • product_config.mk
    包含进了系统中所有AndroidProduct.mk文件,并根据当前产品的配置文件来设置产品编译相关的变量。

重要源码分析:
1)include重要的头文件

include $(BUILD_SYSTEM)/node_fns.mk
include $(BUILD_SYSTEM)/product.mk
include $(BUILD_SYSTEM)/device.mk

注意node_fns.mk这个文件,会将所有的产品mk文件中的变量加上产品文件路径名前缀重新生成新的全局变量,后面会用到。
https://blog.51cto.com/u_15147256/2792449

2)获取所有产品的mk文件

# ---------------------------------------------------------------
# Include the product definitions.
# We need to do this to translate TARGET_PRODUCT into its
# underlying TARGET_DEVICE before we start defining any rules.
#
ifneq ($(strip $(TARGET_BUILD_APPS)),)
# An unbundled app build needs only the core product makefiles.
all_product_configs := $(call get-product-makefiles,\
    $(SRC_TARGET_DIR)/product/AndroidProducts.mk)
else
# Read in all of the product definitions specified by the AndroidProducts.mk
# files in the tree.
all_product_configs := $(get-all-product-makefiles)
endif
#
# Returns the sorted concatenation of all PRODUCT_MAKEFILES
# variables set in all AndroidProducts.mk files.
# $(call ) isn't necessary.
#
define get-all-product-makefiles
$(call get-product-makefiles,$(_find-android-products-files))
endef

_find-android-products-files:获取所有的AndroidProducts.mk文件,包括device、vendor、product等目录下的,以及build/target目录下的。

#
# Returns the list of all AndroidProducts.mk files.
# $(call ) isn't necessary.
#
define _find-android-products-files
$(foreach d, device vendor product,$(call _search-android-products-files-in-dir,$(d))) \
  $(SRC_TARGET_DIR)/product/AndroidProducts.mk
endef

get-product-makefiles:解析_find-android-products-files函数获取的所有的AndroidProducts.mk文件中指定的产品mk文件。

3)获取当前产品的mk文件
以rk3399为例,lunch的时候选择的产品是rk3399_box-userdebug,那么TARGET_PRODUCT=rk3399_box,所以当前产品的mk文件就为rk3399_box.mk。

4)导入所有产品mk文件的变量

# Import all product makefiles.
$(call import-products, $(all_product_makefiles))

# Find the device that this product maps to.
TARGET_DEVICE := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE)

那么TARGET_DEVICE等变量就是在rk3399_box.mk中定义的同名变量。

(六)、product.mk

build/core/product.mk,定义product_config.mk文件中使用的各种函数。

include 和 inherit-product 的区别

假设 PRODUCT_VAR := a 在 A.mk 中, PRODUCT_VAR := b 在 B.mk 中。
如果你在 A.mk 中 include B.mk,你最终会得到 PRODUCT_VAR := b。
但是如果你在 A.mk inherit-product B.mk,你会得到 PRODUCT_VAR := a b。并 inherit-product 确保您不会两次包含同一个 makefile 。

(七)、distdir.mk

build/make/core/distdir.mk

17 # When specifying "dist", the user has asked that we copy the important 18 # files from this build into DIST_DIR.

(八)、dumpvar.mk


0 条评论

发表回复

您的电子邮箱地址不会被公开。