前面分析了Android 4.03编译系统-------envsetup.sh,今天来说lunch命令。
在Android目录下执行了envsetup.sh后,下一部就是执行:lunch。lunch是在envsetup.sh里面定义的函数,函数原型如下:
/**********************************************begin**********************************************/
function lunch()
{
local answer
if [ "$1" ] ; then
answer=$1
else
print_lunch_menu
echo -n "Which would you like? [full-eng] "
read answer
fi
local selection=
if [ -z "$answer" ]
then
selection=full-eng
elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
then
if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
then
selection=${LUNCH_MENU_CHOICES[$(($answer-1))]}
fi
elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")
then
selection=$answer
fi
if [ -z "$selection" ]
then
echo
echo "Invalid lunch combo: $answer"
return 1
fi
export TARGET_BUILD_APPS=
local product=$(echo -n $selection | sed -e "s/-.*$//")
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
local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")
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
export TARGET_PRODUCT=$product
export TARGET_BUILD_VARIANT=$variant
export TARGET_BUILD_TYPE=release
echo
set_stuff_for_environment
printconfig
}
/***********************************************end***********************************************/
函数使用 print_lunch_menu 函数将envsetup.sh 设置的LUNCH_MENU_CHOICES变量打印出来,并等待用户的输入,读入用户输入后,将对应数字的字符串赋值给selection, local product=$(echo -n $selection | sed -e "s/-.*$//") ,local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//"),对selection进行处理,得到product以及variant,前者是将selection中最后一个 - 后面的所有内容去掉而得来,后者刚好相反,是将 - 前面的内容去掉,例如:选择了 4,对应:full_stingray-userdebug,则selection = full_stingray-userdebug,product=full_stingray,variant=userdebug,最后设置环境变量TARGET_PRODUCT=$product等。
到这里,我们设置了一个重要的环境变量TARGET_PRODUCT,这个后面多处都是依靠它来寻找编译文件的。接下是两个函数:set_stuff_for_environment,printconfig.
set_stuff_for_environment 设置了一下环境变量,就不进去分析了。重点是printconfig函数,其函数体比较简单:get_build_var report_config,调用了另外一个函数,get_build_var 也比较简单,重要语句:
make --no-print-directory -C "$T" -f build/core/config.mk dumpvar-$1
它的意思是执行build/core/config.mk,至于dumpvar-$1,我一直没找到,也不知道是什么意思,有知道的告知一下,多谢!
好了,现在开始进入config.mk文件(其实后面的make命名重复执行,不管了,一步步往下走)。config.mk前面那一堆的变量定义赋值就不说了,直奔重点:
/**********************************************begin**********************************************/
include $(BUILD_SYSTEM)/envsetup.mk
board_config_mk := \
$(strip $(wildcard \
$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
device/*/$(TARGET_DEVICE)/BoardConfig.mk \
vendor/*/$(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)
TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))
/**********************************************end**********************************************/
上面这段代码里,有个重要的变量TARGET_DEVICE,它在哪里定义的呢?它在envsetup.mk中定义的,注意,注意,是envsetup.mk,不要和前面的envsetup.sh搞混了,后则位于build/目录下,前者位于build/core/目录下,来看看envsetup.mk,envsetup.mk里面也设置了一大堆的变量,最重要的是:include $(BUILD_SYSTEM)/product_config.mk,
头是不是有点晕了?我也晕了,先整理一下思路:我们是进来寻找TARGET_DEVICE的,前面都没有设置,只能继续跟进了。finally,在product_config.mk中终于找到了TARGET_DEVICE的定义:
/**********************************************begin**********************************************/
ifneq ($(strip $(TARGET_BUILD_APPS)),)
$(call import-products,$(call get-product-makefiles,\
$(SRC_TARGET_DIR)/product/AndroidProducts.mk))
else
$(call import-products, $(get-all-product-makefiles))
endif # TARGET_BUILD_APPS
$(check-all-products)
ifneq ($(filter dump-products, $(MAKECMDGOALS)),)
$(dump-products)
$(error done)
endif
INTERNAL_PRODUCT := $(call resolve-short-product-name, $(TARGET_PRODUCT))
#$(error TARGET_PRODUCT $(TARGET_PRODUCT) --> $(INTERNAL_PRODUCT))
TARGET_DEVICE := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE)
#$(error TARGET_DEVICE $(TARGET_DEVICE) --> $(INTERNAL_PRODUCT))
/***********************************************end***********************************************/
在分析TARGET_DEVICE之前,先说明一下前面的一小段代码,也是很重要的。TARGET_BUILD_APPS等于空,所以执行else分支,$(call import-products, $(get-all-product-makefiles))是读取所有的AndroidProducts.mk,并将其中定义的PRODUCT_MAKEFILES变量都赋值给PRODUCT变量,后面的操作都是在PRODUCT里面寻找需要的文件。好了现在开始分析TARGET_DEVICE。
首先,是INTERNAL_PRODUCT,TARGET_PRODUCT是前面定义的,至于函数resolve-short-product-name,定义再produck.mk文件中,我是没怎么看懂,我也没打算看懂了,因为有个好方法可以知道结果,可以将上面的两个error 打开,这样就能知道结果了:
$(error TARGET_PRODUCT $(TARGET_PRODUCT) --> $(INTERNAL_PRODUCT)) ,输出如下:
TARGET_PRODUCT full_stingray --> device/moto/stingray/full_stingray.mk。
经过测试,根据TARGET_PRODUCT=full_stingray寻找full_stingray.mk的条件是:full_stingray.mk文件中的
PRODUCT_NAME ==TARGET_PRODUCT
$(error TARGET_DEVICE $(TARGET_DEVICE) --> $(INTERNAL_PRODUCT)), 输出如下:
TARGET_DEVICE stingray--> device/moto/stingray/full_stingray.mk。
经过测试,TARGET_DEVICE等于 full_stingray.mk 中的PRODUCT_DEVICE。
好了目标找到了,终于可以回去了,我们回到config.mk文件中:
/**********************************************begin**********************************************/
include $(BUILD_SYSTEM)/envsetup.mk
board_config_mk := \
$(strip $(wildcard \
$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
device/*/$(TARGET_DEVICE)/BoardConfig.mk \
vendor/*/$(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)
TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))
/**********************************************end**********************************************/
board_config_mk 根据千辛万苦得来的TARGET_DEVICE来寻找BoardConfig.mk,里面是一些cpu级别的配置。
终于把lunch的主线说完了,后面的make就简单得多了,因为make做了很多重复的操作。