通过之前介绍的source导入编译源码的相关配置,下面就进入了lunch操作。用户通过source build/envsetup.sh命令后,系统初始化相关变量环境工作,脚本提供一个lunch函数,让用户选择自己想要编译的版本分支,下面我们就来具体分析一下。
lunch选择合适的编译版本
在source流程之后,紧接着就是执行lunch操作,lunch操作执行的其实就是build/envsetup.sh脚本中的lunch函数,下面看看lunch函数如何执行。由于lunch代码众多,这里就不总的例举,下面分模块进行具体分析。
lunch参数处理
读取lunch参数
获取用户编译目标到answer变量,如果用户没有输入参数,调用print_lunch_menu打印可选编译项,提示用户继续输入
local answer
if [ "$1" ] ; then
# lunch后面直接带参数
answer=$1
else
# lunch后面不带参数,则打印处所有的target product和variant菜单提供用户选择
print_lunch_menu
echo -n "Which would you like? [aosp_arm-eng] "
read answer
fi
打印函数print_lunch_menu如下:
#该函数负责打印已经定义的版本
function print_lunch_menu()
{
local uname=$(uname)
echo
echo "You're building on" $uname
echo
echo "Lunch menu... pick a combo:"
local i=1
local choice
for choice in ${LUNCH_MENU_CHOICES[@]}
do
echo " $i. $choice"
# i自增1
i=$(($i+1))
done
echo
}
根据参数,判断选择对应的编译版本
如果判断answer值是否为空,空即默认值缺省值aosp_arm-eng。之后对answer进行异常判断,因为用户输入多样性,需要适当的过滤。
local selection=
if [ -z "$answer" ]
then
# 如果用户在菜单中没有选择,直接回车,则为系统缺省的aosp_arm-eng
selection=aosp_arm-eng
# 不换行输出answer值,静默指定字符匹配开头数字两位内容
elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
then
# 如果answer是选择菜单的数字,则获取该数字对应的字符串
if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
then
selection=${LUNCH_MENU_CHOICES[$(($answer-1))]}
fi
# 如果 answer字符串匹配 *-*模式(*的开头不能为-)
elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")
then
selection=$answer
fi
if [ -z "$selection" ]
then
echo
echo "Invalid lunch combo: $answer"
return 1
fi
筛选过程总结如下:
输入为空,静默值aosp_arm-eng
输入数字,取出数字前两位
输入数字前两位小于编译版本可选项,选定指定数字的编译版本(可选项存在数组,下标为0,需要answer-1)
输入字符串,进行匹配过滤。字符串使用”-”连接,而且”-”连接的前后两个子串中都没有”-”,则认为是板型名称字符串,直接赋给selection
经过筛选,selection值还是空,直接报错。
分离selection中参数product
将 product-variant模式中的product分离出来,类似从字符串”aosp_arm-eng”中取出”aosp_arm”,具体如下:
export TARGET_BUILD_APPS=
# 将 product-variant模式中的product分离出来
local product=$(echo -n $selection | sed -e "s/-.*$//")
# 检查product,调用关系 check_product()->get_build_var()->build/core/config.mk
check_product $product
if [ $? -ne 0 ]
then
echo
echo "** Don't have a product spec for: '$product'"
echo "** Do you have the right repo manifest?"
product=
fi
代码具体流程如下:
echo -n $selection | sed -e “s/-.*$//“
不换行取出用户输入的selection,使用sed -e “s/-.$//“过滤掉selection字符串中”-**“的部分后,例如从”aosp_arm-eng”过滤”-eng”得到”aosp_arm”,赋值到product中
2.调用check_product,传入product赋值到TARGET_PRODUCT,之后流程如下:
# 检查指定的TARGET_PRODUCT是否允许,默认的有sim和generic。如果不允许,则输出错误信息,允许则不显示
function check_product()
{
T=$(gettop)
if [ ! "$T" ]; then
echo "Couldn't locate the top of the tree. Try setting TOP." >&2
return
fi
TARGET_PRODUCT=$1 \
TARGET_BUILD_VARIANT= \
TARGET_BUILD_TYPE= \
TARGET_BUILD_APPS= \
get_build_var TARGET_DEVICE > /dev/null
# hide successful answers, but allow the errors to show
}
# 列出make脚本中某变量的值,当前为build/core/config.mk
function get_build_var()
{
T=$(gettop)
if [ ! "$T" ]; then
echo "Couldn't locate the top of the tree. Try setting TOP." >&2
return
fi
(\cd $T; CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
command make --no-print-directory -f build/core/config.mk dumpvar-$1)
}
T=$(gettop)获取源码顶层目录,这里不做过多解释。下面是get_build_var中比较复杂的部分:
(\cd $T; CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
command make --no-print-directory -f build/core/config.mk dumpvar-$1)
首先进入源码根目录
设置两个静态变量 CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core
shell环境中command命令执行make,指定文件build/core/config.mk
config.mk会include dumpvar.mk,这个文件中会提取我们传入的dumpvar-TARGET_DEVICE变量中的TARGET_DEVICE,打印TARGET_PRODUCT
总之,函数get_build_var实际上就是通过make命令在Android源代码工程根目录中执行build/core/config.mk文件,并且将make目标设置为dumpvar-$1,也就是dumpvar-TARGET_DEVICE。这里函数如果执行成功,便返回0
文件build/core/config.mk的内容比较多,这里我们只关注与产品名称合法性检查相关的逻辑,这些逻辑也基本上涵盖了Android编译系统初始化的逻辑,下面具体分析。
依赖mk文件的导入分析
相关mk文件的导入
build/core/config.mk 中 include 了众多的mk文件,这里例举其中重要的部分:
...
# Various mappings to avoid hard-coding paths all over the place
include $(BUILD_SYSTEM)/pathmap.mk
# ---------------------------------------------------------------
# Define most of the global variables. These are the ones that
# are specific to the user's build configuration.
include $(BUILD_SYSTEM)/envsetup.mk
...
include $(BUILD_SYSTEM)/dumpvar.mk
其中比较重要的是中间的 envsetup.mk 这个文件
envsetup.mk 文件分析
envsetup.mk 文件中逻辑众多,这里我们只关注重要的两个逻辑:AndroidProduct.mk和BoardCong.mk 文件的搜集工作。代码如下:
...
# 搜集AndroidProduct.mk文件
include $(BUILD_SYSTEM)/product_config.mk
# Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)
# or under vendor/*/$(TARGET_DEVICE). Search in both places, but
# make sure only one exists.
# Real boards should always be associated with an OEM vendor.
# 搜集BoardConfig.mk文件
board_config_mk := \
$(strip $(wildcard \
$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
$(shell test -d device && find device -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \
$(shell test -d vendor && find vendor -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \
))
ifeq ($(board_config_mk),)
$(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))
endif
ifneq ($(words $(board_config_mk)),1)
$(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))
endif
include $(board_config_mk)
ifeq ($(TARGET_ARCH),)
$(error TARGET_ARCH not defined by board config: $(board_config_mk))
endif
TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))
board_config_mk :=
...
AndroidProducts.mk文件搜集及TARGET_DEVICE值的确定
envsetup.mk 文件首先会 include product_config.mk文件,这个文件中设置了相关的变量,如下:
# 步骤1
ifneq ($(strip $(TARGET_BUILD_APPS)),)
all_product_configs := $(call get-product-makefiles,\
$(SRC_TARGET_DIR)/product/AndroidProducts.mk)
else
all_product_configs := $(get-all-product-makefiles)
endif
# 步骤2
current_product_makefile :=
all_product_makefiles :=
$(foreach f, $(all_product_configs),\
$(eval _cpm_words := $(subst :,$(space),$(f)))\
$(eval _cpm_word1 := $(word 1,$(_cpm_words)))\
$(eval _cpm_word2 := $(word 2,$(_cpm_words)))\
$(if $(_cpm_word2),\
$(eval all_product_makefiles += $(_cpm_word2))\
$(if $(filter $(TARGET_PRODUCT),$(_cpm_word1)),\
$(eval current_product_makefile += $(_cpm_word2)),),\
$(eval all_product_makefiles += $(f))\
$(if $(filter $(TARGET_PRODUCT),$(basename $(notdir $(f)))),\
$(eval current_product_makefile += $(f)),)))
_cpm_words :=
_cpm_word1 :=
_cpm_word2 :=
current_product_makefile := $(strip $(current_product_makefile))
all_product_makefiles := $(strip $(all_product_makefiles))
# 步骤3
ifneq (,$(filter product-graph dump-products, $(MAKECMDGOALS)))
$(call import-products, $(all_product_makefiles))
else
ifndef current_product_makefile
$(error Can not locate config makefile for product "$(TARGET_PRODUCT)")
endif
ifneq (1,$(words $(current_product_makefile)))
$(error Product "$(TARGET_PRODUCT)" ambiguous: matches $(current_product_makefile))
endif
$(call import-products, $(current_product_makefile))
endif # Import all or just the current product makefile
$(check-all-products)
ifneq ($(filter dump-products, $(MAKECMDGOALS)),)
$(dump-products)
$(error done)
endif
# 步骤4
INTERNAL_PRODUCT := $(call resolve-short-product-name, $(TARGET_PRODUCT))
ifneq ($(current_product_makefile),$(INTERNAL_PRODUCT))
$(error PRODUCT_NAME inconsistent in $(current_product_makefile) and $(INTERNAL_PRODUCT))
endif
current_product_makefile :=
all_product_makefiles :=
all_product_configs :=
# 步骤5
PRODUCT_BOOT_JARS := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_BOOT_JARS))
PRODUCT_SYSTEM_SERVER_JARS := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SYSTEM_SERVER_JARS))
TARGET_DEVICE := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE)
检查环境变量TARGET_BUILD_APPS的值是否等于空。如果不等于空,那么就说明此次编译不是针对整个系统,因此只要将核心的产品相关的Makefile文件加载进来就行了,否则的话,就要将所有与产品相关的Makefile文件加载进来的。核心产品Makefile文件在$(SRC_TARGET_DIR)/product/AndroidProducts.mk文件中指定,也就是在build/target/product/AndroidProducts.mk文件,通过调用函数get-product-makefiles可以获得。所有与产品相关的Makefile文件可以通过另外一个函数get-all-product-makefiles获得。无论如何,最终获得的产品Makefie文件列表保存在变量all_product_configs中。
遍历变量all_product_configs所描述的产品Makefile列表,并且在这些Makefile文件中,找到名称与环境变量TARGET_PRODUCT的值相同的文件,保存在另外一个变量current_product_makefile中,作为需要为当前指定的产品所加载的Makefile文件列表。在这个过程当中,上一步找到的所有的产品Makefile文件也会保存在变量all_product_makefiles中。注意,环境变量TARGET_PRODUCT的值是在我们执行lunch命令的时候设置并且传递进来的。
如果指定的make目标等于product-graph或者dump-products,那么就将所有的产品相关的Makefile文件加载进来,否则的话,只加载与目标产品相关的Makefile文件。从前面的分析可以知道,此时的make目标为dumpvar-TARGET_DEVICE,因此接下来只会加载与目标产品,即$(TARGET_PRODUCT),相关的Makefile文件,这是通过调用另外一个函数import-products实现的。
调用函数resolve-short-product-name解析环境变量TARGET_PRODUCT的值,将它变成一个Makefile文件路径。并且保存在变量INTERNAL_PRODUCT中。这里要求变量INTERNAL_PRODUCT和current_product_makefile的值相等,否则的话,就说明用户指定了一个非法的产品名称。
找到一个名称为PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE的变量,并且将它的值保存另外一个变量TARGET_DEVICE中。变量PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE是在加载产品Makefile文件的过程中定义的,用来描述当前指定的产品的名称
上述过程主要涉及到了get-all-product-makefiles、import-products和resolve-short-product-name三个关键函数,理解它们的执行过程对理解Android编译系统的初始化过程很有帮助,接下来我们分别分析它们的实现。
查找product相关文件的mk实现
其主要方法集中在 build/core/product.mk 文件中,下面具体分析:
get-all-product-makefiles的具体实现
define get-all-product-makefiles $(call get-product-makefiles,$(_find-android-products-files)) endef
首先是调用函数_find-android-products-files来找到Android源代码目录中定义的所有AndroidProducts.mk文件,然后再调用函数get-product-makefiles获得在这里AndroidProducts.mk文件里面定义的产品Makefile文件。
_find-android-products-files的具体实现
define _find-android-products-files $(shell test -d device && find device -maxdepth 6 -name AndroidProducts.mk) \ $(shell test -d vendor && find vendor -maxdepth 6 -name AndroidProducts.mk) \ $(SRC_TARGET_DIR)/product/AndroidProducts.mk endef
从这里就可以看出,Android源代码目录中定义的所有AndroidProducts.mk文件位于device、vendor或者build/target/product目录或者相应的子目录(最深是6层)中。
get-product-makefiles的具体实现
define get-product-makefiles $(sort \ $(foreach f,$(1), \ $(eval PRODUCT_MAKEFILES :=) \ $(eval LOCAL_DIR := $(patsubst %/,%,$(dir $(f)))) \ $(eval include $(f)) \ $(PRODUCT_MAKEFILES) \ ) \ $(eval PRODUCT_MAKEFILES :=) \ $(eval LOCAL_DIR :=) \ ) endef
这个函数实际上就是遍历参数$1所描述的AndroidProucts.mk文件列表,并且将定义在这些AndroidProucts.mk文件中的变量PRODUCT_MAKEFILES的值提取出来,形成一个列表返回给调用者。
例如:在build/target/product/AndroidProducts.mk文件中,变量PRODUCT_MAKEFILES的值如下所示:
ifneq ($(TARGET_BUILD_APPS),) PRODUCT_MAKEFILES := \ $(LOCAL_DIR)/aosp_arm.mk \ $(LOCAL_DIR)/full.mk \ ... $(LOCAL_DIR)/aosp_mips64.mk \ $(LOCAL_DIR)/aosp_x86_64.mk else PRODUCT_MAKEFILES := \ $(LOCAL_DIR)/core.mk \ $(LOCAL_DIR)/generic.mk \ $(LOCAL_DIR)/generic_x86.mk \ ... endif
这里列出的每一个文件都对应于一个产品
import-products的具体实现
define import-products $(call import-nodes,PRODUCTS,$(1),$(_product_var_list)) endef
它调用另外一个函数import-nodes来加载由参数$1所指定的产品Makefile文件,并且指定了另外两个参数PRODUCTS和$(_product_var_list)。其中,变量_product_var_list也是定义在文件build/core/product.mk中,它的值如下所示:
_product_var_list := \ PRODUCT_NAME \ PRODUCT_MODEL \ PRODUCT_LOCALES \ PRODUCT_AAPT_CONFIG \ PRODUCT_AAPT_PREF_CONFIG \ PRODUCT_AAPT_PREBUILT_DPI \ PRODUCT_PACKAGES \ PRODUCT_PACKAGES_DEBUG \ PRODUCT_PACKAGES_ENG \ PRODUCT_PACKAGES_TESTS \ PRODUCT_DEVICE \ ...
它描述的是在产品Makefile文件中定义在各种变量。import-nodes定义在文件build/core/node_fns.mk中,如下所示:
# # $(1): output list variable name, like "PRODUCTS" or "DEVICES" # $(2): list of makefiles representing nodes to import # $(3): list of node variable names # define import-nodes $(if \ $(foreach _in,$(2), \ $(eval _node_import_context := _nic.$(1).[[$(_in)]]) \ $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \ should be empty here: $(_include_stack))),) \ $(eval _include_stack := ) \ $(call _import-nodes-inner,$(_node_import_context),$(_in),$(3)) \ $(call move-var-list,$(_node_import_context).$(_in),$(1).$(_in),$(3)) \ $(eval _node_import_context :=) \ $(eval $(1) := $($(1)) $(_in)) \ $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \ should be empty here: $(_include_stack))),) \ ) \ ,) endef
调用函数_import-nodes-inner将参数$2描述的每一个产品Makefile文件加载进来。
调用函数move-var-list将定义在前面所加载的产品Makefile文件里面的由参数$3指定的变量的值分别拷贝到另外一组独立的变量中。
将参数$2描述的每一个产品Makefile文件路径以空格分隔保存在参数$1所描述的变量中,也就是保存在变量PRODUCTS中。
上述第二件事情需要进一步解释一下。由于当前加载的每一个文件都会定义相同的变量,为了区分这些变量,我们需要在这些变量前面加一些前缀。例如,假设加载了build/target/product/full.mk这个产品Makefile文件,它里面定义了以下几个变量:
PRODUCT_NAME := full
PRODUCT_DEVICE := generic
PRODUCT_BRAND := Android
PRODUCT_MODEL := Full Android on Emulator当调用了函数move-var-list对它进行解析后,就会得到以下的新变量:
PRODUCTS.build/target/product/full.mk.PRODUCT_NAME := full
PRODUCTS.build/target/product/full.mk.PRODUCT_DEVICE := generic
PRODUCTS.build/target/product/full.mk.PRODUCT_BRAND := Android
PRODUCTS.build/target/product/full.mk.PRODUCT_MODEL := Full Android on Emulator正是由于调用了函数move-var-list,我们在build/core/product_config.mk文件中可以通过PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE来设置变量TARGET_DEVICE的值,这样就确定了PRODUCT_DEVICE的值。
BoardConfig.mk文件的加载过程
分析完AndroidProduct.mk文件的加载,回到build/core/config.mk文件中,接下来我们再看BoardConfig.mk文件的加载过程。
BoardConfig.mk文件加载
# Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)
# or under vendor/*/$(TARGET_DEVICE). Search in both places, but
# make sure only one exists.
# Real boards should always be associated with an OEM vendor.
# 搜集BoardConfig.mk文件
board_config_mk := \
$(strip $(wildcard \
$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
$(shell test -d device && find device -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \
$(shell test -d vendor && find vendor -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \
))
ifeq ($(board_config_mk),)
$(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))
endif
ifneq ($(words $(board_config_mk)),1)
$(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))
endif
include $(board_config_mk)
ifeq ($(TARGET_ARCH),)
$(error TARGET_ARCH not defined by board config: $(board_config_mk))
endif
TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))
board_config_mk :=
其中注释内容已经说的很明白了,board_config_mk 实现了查找BoardConfig.mk文件的具体方法。对应的文件的路径存在两个路径下:(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)和vendor/*/$(TARGET_DEVICE)。
strip $(wildcard \
$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \ $(***))
wildcard 表示扩展通配符,将(***)内容匹配到$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk形式中去
strip 表示去除结果中的空字符串
shell test -d device && find device -maxdepth 4 -path ‘*/$(TARGET_DEVICE)/BoardConfig.mk’
shell脚本检查device是否为文件夹,之后查找其内部深度最大4级下,用户选定的编译版本(TARGET_DEVICE)目录下是否存在BoardConfig.mk文件。
BoardConfig.mk文件的位置主要就是由环境变量TARGET_DEVICE来确定,它是用来描述目标产品的硬件模块信息的,例如CPU体系结构。环境变量TARGET_DEVICE用来描述目标设备,它的值是在envsetup.mk文件加载的过程中确定的。一旦目标设备确定后,就可以在$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)、device//$(TARGET_DEVICE)和vendor//$(TARGET_DEVICE)目录中找到对应的BoradConfig.mk文件。注意,变量SRC_TARGET_DIR的值等于build/target。
小结依赖mk文件的导入
build/core/config.mk 通过 include 操作依赖众多相关mk文件,比较重要的有以下几个:
build/envsetup.mk build/dumpvar.mk
其中envsetup.mk包含了:
build/product_config.mk build/board_config.mk
product_config.mk 中实现了product相关方法的实现,完成 AndroidProduct.mk 文件的查找和解析,确定了 TARGET_DECVICE 。其依赖的mk有:
build/node_fns.mk build/product.mk build/device.mk
board_config.mk 中实现了 BoardConfig.mk 文件的查找,确定主板配置的相关编译信息。
继续分析lunch参数的处理
分离selection中参数variant
接着上面从selection中分离参数product后,便执行分离variant参数的操作,流程如下:
# 将 product-variant模式中的variant分离出来
local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")
# 检查之,看看是否在 (user userdebug eng) 范围内
check_variant $variant
if [ $? -ne 0 ]
then
echo
echo "** Invalid variant: '$variant'"
echo "** Must be one of ${VARIANT_CHOICES[@]}"
variant=
fi
if [ -z "$product" -o -z "$variant" ]
then
echo
return 1
fi
步骤分析如下:
echo -n $selection | sed -e “s/^[^-]*-//“
获取过滤字符串中最后一个不是以”-“结尾,且删除”-“之前字符串后剩下的字符串
riant $variant
function check_variant() { for v in ${VARIANT_CHOICES[@]} do if [ "$v" = "$1" ] then return 0 fi done return 1 }
检查获取的variant值是否在可选编译项中,在则返回0,否则返回1
导出变量
export TARGET_PRODUCT=$product
export TARGET_BUILD_VARIANT=$variant
export TARGET_BUILD_TYPE=release
echo
代码执行到此处,导出三个系统全局变量TARGET_PRODUCT,TARGET_BUILD_VARIANT,TARGET_BUILD_TYPE
设置其他编译环境,打印配置信息
到这里,基本就完成了编译选项的信息采集,下面最后的工作,设置编译环境参数和打印配置信息
设置其他编译环境
set_stuff_for_environment
printconfig
set_stuff_for_environment 补充剩余环境参数
# 设置android编译需要的环境变量
function set_stuff_for_environment()
{
settitle
set_java_home
setpaths
set_sequence_number
# MStar Android Patch Begin
set_ota
# MStar Android Patch End
export ANDROID_BUILD_TOP=$(gettop)
# With this environment variable new GCC can apply colors to warnings/errors
export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
export ASAN_OPTIONS=detect_leaks=0
}
此函数继续补充编译中的一些其他变量。其中set_java_home会检查导出JAVA_HOME这个环境变量,这个环境变量就是JDK所在的路径;setpaths函数会给PATH环境变量补充编译Android需要的一些路径;set_ota则是本源码分支(MSTAR电视系统)配置ota升级文件的相关配置
打印配置信息
调用 get_build_var函数,参数 report_config,来打印配置信息
function printconfig()
{
T=$(gettop)
if [ ! "$T" ]; then
echo "Couldn't locate the top of the tree. Try setting TOP." >&2
return
fi
get_build_var report_config
}
至此,lunch流程就分析完毕了。
make正式编译系统
具体流程请待下节分析。
说明
本内容针对 MASTR定制的安卓6.0电视系统,本人能力有限,在分析过程查找了相关的资料,比较有参考价值的在下面链接中提到。
老罗的Android之旅