目录
参考资料
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中重要的变量
- BOARD_VENDOR_KERNEL_MODULES
此变量提供了用于供应商映像的内核模块的完整列表。作用:此变量中列出的模块会被复制到供应商映像的 /lib/modules/ 中,在 Android 中装载后会出现在 /vendor/lib/modules 中。
https://source.android.google.cn/docs/core/architecture/kernel/loadable-kernel-modules?hl=zh-cn
https://blog.csdn.net/u010503912/article/details/131847455
最新的 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
作用:将源码中的文件拷贝到编译好的分区文件中,如预安装apk等。
解析位置:main.mk文件同级的Makefile文件
PRODUCT_COPY_FILES原理解析:https://0xforee.top/2016/02/24/android-build-system-parse-PRODUCT_COPY_FILES/
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/
- PRODUCT_PACKAGES(将要安装的 APK 和模块列表)
作用:添加模块进行编译,这里的模块包括APK或者其它的库(任何通过Android.mk生成的目标文件)等。
https://source.android.google.cn/docs/setup/create/new-device?hl=da
https://blog.csdn.net/engine_men/article/details/103958016
https://blog.csdn.net/u014135607/article/details/79995741
https://blog.csdn.net/u012041204/article/details/78425793
(一)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文件中使用的各种函数。
- inherit-product
会将两个mk文件中的相同变量值合并
https://codeleading.com/article/75934806818/
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.
0 条评论