一、android build system编译系统概述
二、envsetup.sh脚本(Android10)
三、soong_ui.bash脚本
四、soong_ui的main.go
五、kati中调用main.mk
六、make目录中的main.mk
七、AndroidProducts.mk
八、module-info.json
九、子模块的执行
AndroidProducts.mk是什么
AndroidProducts.mk
中定义了lunch-combo
和lunch-combo
对应的PRODUCT_MAKEFILES
Makefile文件。
简单来说就是:
lunch
命令执行之后出来那些东西,就看AndroidProducts.mk
中COMMON_LUNCH_CHOICES
定义了啥lunch xxx
执行之后,走那个product config
Makefile文件,就看AndroidProducts.mk
中PRODUCT_MAKEFILES
定义了啥
此外注意一下:
-
COMMON_LUNCH_CHOICES
中定义的产品名需要和PRODUCT_MAKEFILES
中定义的文件名(不考虑路径和.mk
后缀)一样。 -
如果不一样,需要在定义
PRODUCT_MAKEFILES
采用<product_name>:<path_to_the_product_makefile>
格式
例子
/device/google/cuttlefish/AndroidProducts.mk
中是AndroidProducts.mk
PRODUCT_MAKEFILES := \
aosp_cf_arm64_phone:$(LOCAL_DIR)/vsoc_arm64/phone/device.mk \
aosp_cf_x86_64_phone:$(LOCAL_DIR)/vsoc_x86_64/phone/device.mk \
aosp_cf_x86_auto:$(LOCAL_DIR)/vsoc_x86/auto/device.mk \
aosp_cf_x86_pasan:$(LOCAL_DIR)/vsoc_x86/pasan/device.mk \
aosp_cf_x86_phone:$(LOCAL_DIR)/vsoc_x86/phone/device.mk \
aosp_cf_x86_go_phone:$(LOCAL_DIR)/vsoc_x86/go_phone/device.mk \
aosp_cf_x86_gsi:$(LOCAL_DIR)/vsoc_x86/gsi/aosp_cf_x86_gsi.mk \
aosp_cf_x86_tv:$(LOCAL_DIR)/vsoc_x86/tv/device.mk
COMMON_LUNCH_CHOICES := \
aosp_cf_arm64_phone-userdebug \
aosp_cf_x86_64_phone-userdebug \
aosp_cf_x86_auto-userdebug \
aosp_cf_x86_phone-userdebug \
aosp_cf_x86_tv-userdebug
AndroidProducts.mk怎么用
一般来说,AndroidProducts.mk
中就定义两个变量PRODUCT_MAKEFILES
和COMMON_LUNCH_CHOICES
。
尽量不要瞎几把乱加别的东西
以/build/target/product/AndroidProducts.mk
为例子
ifneq ($(TARGET_BUILD_APPS),)
PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/aosp_arm64.mk \
$(LOCAL_DIR)/aosp_arm.mk \
$(LOCAL_DIR)/aosp_x86_64.mk \
$(LOCAL_DIR)/aosp_x86.mk \
$(LOCAL_DIR)/full.mk \
$(LOCAL_DIR)/full_x86.mk \
else
PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/aosp_arm64_ab.mk \
$(LOCAL_DIR)/aosp_arm64.mk \
$(LOCAL_DIR)/aosp_arm_ab.mk \
$(LOCAL_DIR)/aosp_arm.mk \
$(LOCAL_DIR)/aosp_x86_64_ab.mk \
$(LOCAL_DIR)/aosp_x86_64.mk \
$(LOCAL_DIR)/aosp_x86_ab.mk \
$(LOCAL_DIR)/aosp_x86_arm.mk \
$(LOCAL_DIR)/aosp_x86.mk \
$(LOCAL_DIR)/full.mk \
$(LOCAL_DIR)/full_x86.mk \
$(LOCAL_DIR)/generic.mk \
$(LOCAL_DIR)/generic_x86.mk \
$(LOCAL_DIR)/gsi_arm64.mk \
$(LOCAL_DIR)/mainline_arm64.mk \
$(LOCAL_DIR)/mainline_system_arm64.mk \
$(LOCAL_DIR)/sdk_arm64.mk \
$(LOCAL_DIR)/sdk.mk \
$(LOCAL_DIR)/sdk_phone_arm64.mk \
$(LOCAL_DIR)/sdk_phone_armv7.mk \
$(LOCAL_DIR)/sdk_phone_x86_64.mk \
$(LOCAL_DIR)/sdk_phone_x86.mk \
$(LOCAL_DIR)/sdk_x86_64.mk \
$(LOCAL_DIR)/sdk_x86.mk \
endif
COMMON_LUNCH_CHOICES := \
aosp_arm64-eng \
aosp_arm-eng \
aosp_x86_64-eng \
aosp_x86-eng \
从上面的配置可知,google在AOSP源码中加了如下四个默认的lunch-combo配置
aosp_arm64-eng
aosp_arm-eng
aosp_x86_64-eng
aosp_x86-eng
对应的PRODUCT_MAKEFILES
会根据TARGET_BUILD_APPS
而变动
lunch-combo
会用-
分隔,前面的是项目名、后面是版本类型(user
、userdebug
、eng
)
AndroidProducts.mk如何被加载
从lunch
到AndroidProducts.mk.list
-
当用户执行
lunch
命令时会调用get_build_var
、build_build_var_cache
等shell函数 -
这些shell函数又会调用
build/soong/soong_ui.bash --dumpvar-mode
-
soong_ui.bash
走到/build/soong/cmd/soong_ui/main.go
中的main
函数中 -
main
函数中调用build.FindSources(buildCtx, config, f)
-
FindSources
代码逻辑如下
在device
、vendor
、product
目录中查找AndroidProducts.mk
文件。
并将所有的名为AndroidProducts.mk
文件路径记录在AndroidProducts.mk.list
中
备注:这函数的作用不止如此,还会查找Android.mk
、Android.bp
、CleanSpec.mk
等文件。
具体代码如下:
// FindSources searches for source files known to <f> and writes them to the filesystem for
// use later.
func FindSources(ctx Context, config Config, f *finder.Finder) {
// note that dumpDir in FindSources may be different than dumpDir in NewSourceFinder
// if a caller such as multiproduct_kati wants to share one Finder among several builds
dumpDir := config.FileListDir()
os.MkdirAll(dumpDir, 0777)
androidMks := f.FindFirstNamedAt(".", "Android.mk")
err := dumpListToFile(androidMks, filepath.Join(dumpDir, "Android.mk.list"))
if err != nil {
ctx.Fatalf("Could not export module list: %v", err)
}
androidProductsMks := f.FindNamedAt("device", "AndroidProducts.mk")
androidProductsMks = append(androidProductsMks, f.FindNamedAt("vendor", "AndroidProducts.mk")...)
androidProductsMks = append(androidProductsMks, f.FindNamedAt("product", "AndroidProducts.mk")...)
err = dumpListToFile(androidProductsMks, filepath.Join(dumpDir, "AndroidProducts.mk.list"))
if err != nil {
ctx.Fatalf("Could not export product list: %v", err)
}
cleanSpecs := f.FindFirstNamedAt(".", "CleanSpec.mk")
err = dumpListToFile(cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
if err != nil {
ctx.Fatalf("Could not export module list: %v", err)
}
owners := f.FindNamedAt(".", "OWNERS")
err = dumpListToFile(owners, filepath.Join(dumpDir, "OWNERS.list"))
if err != nil {
ctx.Fatalf("Could not find OWNERS: %v", err)
}
testMappings := f.FindNamedAt(".", "TEST_MAPPING")
err = dumpListToFile(testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list"))
if err != nil {
ctx.Fatalf("Could not find TEST_MAPPING: %v", err)
}
androidBps := f.FindNamedAt(".", "Android.bp")
androidBps = append(androidBps, f.FindNamedAt("build/blueprint", "Blueprints")...)
if len(androidBps) == 0 {
ctx.Fatalf("No Android.bp found")
}
err = dumpListToFile(androidBps, filepath.Join(dumpDir, "Android.bp.list"))
if err != nil {
ctx.Fatalf("Could not find modules: %v", err)
}
}
AndroidProducts.mk.list
调用关系
AndroidProducts.mk.list
会在makefile宏函数_find-android-products-files
中被调用
_find-android-products-files
宏函数
/build/make/core/product.mk
文件中定义了该宏函数
代码如下:
#
# Returns the list of all AndroidProducts.mk files.
# $(call ) isn't necessary.
#
define _find-android-products-files
$(file <$(OUT_DIR)/.module_paths/AndroidProducts.mk.list) \
$(SRC_TARGET_DIR)/product/AndroidProducts.mk
endef
$(_find-android-products-files)
显示了所有AndroidProducts.mk
的路径信息
get-all-product-makefiles
宏函数
$(_find-android-products-files)
被get-all-product-makefiles
调用
代码如下:
#
# 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
get-all-product-makefiles
中,$(_find-android-products-files)
作为参数,交给get-product-makefiles
去处理
get-product-makefiles
宏函数
#
# Returns the sorted concatenation of PRODUCT_MAKEFILES
# variables set in the given AndroidProducts.mk files.
# $(1): the list of AndroidProducts.mk files.
#
# As a side-effect, COMMON_LUNCH_CHOICES will be set to a
# union of all of the COMMON_LUNCH_CHOICES definitions within
# each AndroidProducts.mk file.
#
define get-product-makefiles
$(sort \
$(eval _COMMON_LUNCH_CHOICES :=) \
$(foreach f,$(1), \
$(eval PRODUCT_MAKEFILES :=) \
$(eval COMMON_LUNCH_CHOICES :=) \
$(eval LOCAL_DIR := $(patsubst %/,%,$(dir $(f)))) \
$(eval include $(f)) \
$(call _validate-common-lunch-choices,$(COMMON_LUNCH_CHOICES),$(PRODUCT_MAKEFILES)) \
$(eval _COMMON_LUNCH_CHOICES += $(COMMON_LUNCH_CHOICES)) \
$(PRODUCT_MAKEFILES) \
) \
$(eval PRODUCT_MAKEFILES :=) \
$(eval LOCAL_DIR :=) \
$(eval COMMON_LUNCH_CHOICES := $(sort $(_COMMON_LUNCH_CHOICES))) \
$(eval _COMMON_LUNCH_CHOICES :=) \
)
endef
通过foreach
循环遍历$(_find-android-products-files)
然后依次include AndroidProducts.mk
,然后通过_validate-common-lunch-choices
宏函数,判断其是否有效
最终结果:
- 函数返回所有
AndroidProducts.mk
中定义的PRODUCT_MAKEFILES
的值(全都囊括) - 更新
COMMON_LUNCH_CHOICES
变量的值,将所有AndroidProducts.mk
中定义的COMMON_LUNCH_CHOICES
的值都囊括
_validate-common-lunch-choices
宏函数
$(1): The new list of COMMON_LUNCH_CHOICES
$(2): The new list of PRODUCT_MAKEFILES
通过foreach
循环遍历$(COMMON_LUNCH_CHOICES)
即$(1)
然后将其中的-
替换为空格,然后通过
words
(内置宏函数)判断有几个单词
-
如果小于2,是有问题的;
-
如果大于等于4,是有问题的;
-
如果版本不是
eng userdebug user
三个中的某一个,是有问题的; -
如果产品名字在
$(PRODUCT_MAKEFILES)
中没有,是有问题的;
备注:_decode-product-name
函数会去解析$(PRODUCT_MAKEFILES)
遍历
#
# Validates the new common lunch choices -- ensures that they're in an
# appropriate form, and are paired with definitions of their products.
# $(1): The new list of COMMON_LUNCH_CHOICES
# $(2): The new list of PRODUCT_MAKEFILES
#
define _validate-common-lunch-choices
$(strip $(foreach choice,$(1),\
$(eval _parts := $(subst -,$(space),$(choice))) \
$(if $(call math_lt,$(words $(_parts)),2), \
$(error $(LOCAL_DIR): $(choice): Invalid lunch choice)) \
$(if $(call math_gt_or_eq,$(words $(_parts)),4), \
$(error $(LOCAL_DIR): $(choice): Invalid lunch choice)) \
$(if $(filter-out eng userdebug user,$(word 2,$(_parts))), \
$(error $(LOCAL_DIR): $(choice): Invalid variant: $(word 2,$(_parts)))) \
$(if $(filter-out $(foreach p,$(2),$(call _decode-product-name,$(p))),$(word 1,$(_parts))), \
$(error $(LOCAL_DIR): $(word 1,$(_parts)): Product not defined in this file)) \
))
endef
_decode-product-name
宏函数
本宏函数,会去对传进来的参数进行解析:
先把传进来的参数$1中的`:`换为` `空格
如果,有两个word(即有冒号):
返回替换后的传参$1
反之:
返回$(basename $(notdir $(1))) 和 $(1)
返回值的格式如下:
# Returns two words:
# <name> <file>
源码如下:
#
# For entries returned by get-product-makefiles, decode an entry to a short
# product name. These either may be in the form of <name>:path/to/file.mk or
# path/to/<name>.mk
# $(1): The entry to decode
#
# Returns two words:
# <name> <file>
#
define _decode-product-name
$(strip \
$(eval _cpm_words := $(subst :,$(space),$(1))) \
$(if $(word 2,$(_cpm_words)), \
$(wordlist 1,2,$(_cpm_words)), \
$(basename $(notdir $(1))) $(1)))
endef
get-all-product-makefiles
调用的地方
/build/make/core/product_config.mk
中调用了get-all-product-makefiles
# Read in all of the product definitions specified by the AndroidProducts.mk
# files in the tree.
all_product_configs := $(get-all-product-makefiles)
all_product_configs
变量里面存放了get-all-product-makefiles
宏函数的返回值。
即,所有的AndroidProducts.mk
中定义的PRODUCT_MAKEFILES
的值(全都囊括)
all_product_configs
变量
如下代码会检查TARGET_PRODUCT
的值是不是有效的(即实际存在的),并放在current_product_makefile
变量中
总结:
all_named_products 存放所有的产品名字
all_product_makefiles 存放所有的product makefile
current_product_makefile 存放当前使用的product makefile
代码如下:
# Find the product config makefile for the current product.
# all_product_configs consists items like:
# <product_name>:<path_to_the_product_makefile>
# or just <path_to_the_product_makefile> in case the product name is the
# same as the base filename of the product config makefile.
current_product_makefile :=
all_product_makefiles :=
$(foreach f, $(all_product_configs),\
$(eval _cpm_words := $(call _decode-product-name,$(f)))\
$(eval _cpm_word1 := $(word 1,$(_cpm_words)))\
$(eval _cpm_word2 := $(word 2,$(_cpm_words)))\
$(eval all_product_makefiles += $(_cpm_word2))\
$(eval all_named_products += $(_cpm_word1))\
$(if $(filter $(TARGET_PRODUCT),$(_cpm_word1)),\
$(eval current_product_makefile += $(_cpm_word2)),))
_cpm_words :=
_cpm_word1 :=
_cpm_word2 :=
current_product_makefile := $(strip $(current_product_makefile))
all_product_makefiles := $(strip $(all_product_makefiles))