您的位置:首页 > 移动开发 > Android开发

Android lunch分析以及产品分支构建

2014-09-19 08:31 417 查看
一、背景

随着Android应用范围越来越广泛,用户对Android的需求也越来越趋于复杂,在开发Android应用以及底层产品驱动时,往往两套产品所需要的硬件平台就大不相同,而软件功能却相差不大。在这种纷繁的情况下,是否每种产品就需要一套源代码来维护。如果真这么做的话,那工作量就太大了。所以提出了产品分支的概念。在Android源码中提供了这么一种强大的功能。

公司正在Android上针对硬件平台做调整和开发,手上现有多种方案,有的需要无线wifi,有的底层芯片不一致。在这种情况下,公司使用Android lunch的方式使用一套源代码维护多种方案。

二、文档目的

当前网络上没有成体系的对Android lunch调用关系的研究,所以将Android lunch分析以及产品分支构建这份文档共享出来以帮助对此过程不了解的开发者。如有疑问和建议,请留言。

三、Android lunch调用关系

1. 调用流程图:



2. 调用关系分析

在编译Android产品的时候我们首先会导入. build/envsetup.sh ,然后lunch,这时候我们可以看到几个基本的产品版本。

从现象回推,首先查看envsetup.sh文件中的lunch函数:

具体分析见:lunch函数分析

在envsetup.sh中,有几个比较重要的函数。Lunch(),check_product(),print_lunch_menu(),get_build_var(),gettop,add_lunch_combo(),set_stuff_for_environment(),set_java_home(),findmakefile()。

1) lunch

[plain]
view plaincopy

functionlunch()
{
local answer
if [ "$1" ] ; then
# lunch后面直接带参数
answer=$1
else
# lunch后面不带参数,则打印处所有的target product和variant菜单提供用户选择
print_lunch_menu
echo -n "Which would you like?[generic-eng] "
read answer
fi
local selection=
if [ -z "$answer" ]
then
# 如果用户在菜单中没有选择,直接回车,则为系统缺省的generic-eng
selection=generic-eng
elif [ "$answer" ="simulator" ]
then
# 如果是模拟器
selection=simulator
elif(echo -n $answer | grep -q -e "^[0-9][0-9]*{1}quot;)
then
# 如果answer是选择菜单的数字,则获取该数字对应的字符串
if [ $answer -le${#LUNCH_MENU_CHOICES[@]} ]
then
selection=${LUNCH_MENU_CHOICES[$(($answer-$_arrayoffset))]}
fi
# 如果 answer字符串匹配 *-*模式(*的开头不能为-)
elif (echo -n $answer | grep -q -e"^[^\-][^\-]*-[^\-][^\-]*{1}quot;)

then
selection=$answer
fi

if [ -z "$selection" ]
then
echo
echo "Invalid lunch combo:$answer"
return 1
fi

# special case the simulator
if [ "$selection" ="simulator" ]
then
# 模拟器模式
export TARGET_PRODUCT=sim
export TARGET_BUILD_VARIANT=eng
export TARGET_SIMULATOR=true
export TARGET_BUILD_TYPE=debug
else

# 将 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 productspec for: '$product'"
echo "** Do you have the rightrepo manifest?"
product=
fi

# 将 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

export TARGET_PRODUCT=$product
export TARGET_BUILD_VARIANT=$variant
export TARGET_SIMULATOR=false
export TARGET_BUILD_TYPE=release
fi # !simulator


2) Check_product:

[plain]
view plaincopy

functioncheck_product()
{
T=$(gettop)
if [ ! "$T" ]; then
echo "Couldn't locate the top ofthe tree. Try setting TOP.">&2
return
fi
CALLED_FROM_SETUP=trueBUILD_SYSTEM=build/core \
TARGET_PRODUCT=$1 TARGET_BUILD_VARIANT=\
TARGET_SIMULATOR= TARGET_BUILD_TYPE= \
TARGET_BUILD_APPS= \
get_build_varTARGET_DEVICE > /dev/null
# hide successful answers, but allow theerrors to show
}

检查指定的TARGET_PRODUCT是否允许,默认的有sim和generic。如果不允许,则输出错误信息,允许则不显示
3) Print_lunch_menu()

[plain]
view plaincopy

functionprint_lunch_menu()
{
local uname=$(uname)
echo
echo "You're building on" $uname
echo
echo "Lunch menu... pick acombo:"

local i=1
local choice
#循环查找LUNCH_MENU_CHOICES中的版本
for choice in ${LUNCH_MENU_CHOICES[@]}
do
echo " $i. $choice"
i=$(($i+1))
done

echo
}

该函数负责打印已经定义的版本
4) Get_build_var()

[plain]
view plaincopy

functionget_build_var()
{
T=$(gettop)
if [ ! "$T" ]; then
echo "Couldn't locate the top ofthe tree. Try setting TOP.">&2
return
fi
CALLED_FROM_SETUP=trueBUILD_SYSTEM=build/core \
make --no-print-directory -C"$T" -f build/core/config.mk dumpvar-$1
}

列出make脚本中某变量的值,当前为build/core/config.mk
5) Gettop()

[plain]
view plaincopy

function gettop
{
local TOPFILE=build/core/envsetup.mk
if [ -n "$TOP" -a -f"$TOP/$TOPFILE" ] ; then
echo $TOP
# 如果TOP不为空,并且envsetup.mk存在,函数直接返回TOP对应路径
else
# 否则(指找不到envsetup.mk)如下处理
if [ -f $TOPFILE ] ; then
# The following circumlocution(repeated below as well) ensures
# that we record the true directoryname and not one that is
# faked up with symlink names.
PWD= /bin/pwd
else
# We redirect cd to /dev/null incase it's aliased to
# a command that prints somethingas a side-effect
# (like pushd)
local HERE=$PWD
T=
while [ \( ! \( -f $TOPFILE \) \)-a \( $PWD != "/" \) ]; do
cd .. > /dev/null
T=`PWD= /bin/pwd`
done
cd $HERE > /dev/null
if [ -f "$T/$TOPFILE" ];then
echo $T
fi
fi
fi
}

返回当前android代码树的顶层路径。前提是当前路径位于android代码树中
6) Add_lunch_combo()

[plain]
view plaincopy

functionadd_lunch_combo()
{
local new_combo=$1
local c
for c in ${LUNCH_MENU_CHOICES[@]} ; do
if [ "$new_combo" ="$c" ] ; then
return
fi
done
LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
}

向环境变量LUNCH_MENU_CHOICES标识的列表中添加项
7) Set_stuff_for_environment()

[plain]
view plaincopy

functionset_stuff_for_environment()
{
settitle
set_java_home
setpaths
set_sequence_number

export ANDROID_BUILD_TOP=$(gettop)
}

设置android编译需要的环境变量
8) Set_java_home

[plain]
view plaincopy

functionset_java_home() {
if [ ! "$J***A_HOME" ]; then
case `uname -s` in
Darwin)
exportJ***A_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home
;;
*)
exportJ***A_HOME=/usr/lib/jvm/java-6-sun
;;
esac
fi
}

设置java运行环境
9) findmakefile

[plain]
view plaincopy

functionfindmakefile()
{
TOPFILE=build/core/envsetup.mk
# We redirect cd to /dev/null in case it's aliased to
# a command that prints something as a side-effect
# (like pushd)
local HERE=$PWD
T=
while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD !="/" \) ]; do
T=$PWD
if [ -f "$T/Android.mk" ];then
echo$T/Android.mk
# 如果找到Android.mk,echo出来的全路径将作为函数的返回值赋给某个变量
cd $HERE> /dev/null
return
fi
cd .. > /dev/null
done
cd $HERE > /dev/null
}

此外还有一段比较重要的代码

[plain]
view plaincopy

# Execute thecontents of any vendorsetup.sh files we can find.
for f in`/bin/ls vendor/*/vendorsetup.sh vendor/*/build/vendorsetup.sh device/*/*/vendorsetup.sh2> /dev/null`
do
echo "including $f"
. $f
done
unset f

这段代码寻找vendor,build,device指定目录下的vendorsetup.sh。在vendorsetup.sh中定义了各版本的产品。



注:/dev/null 是 Unix/Linux 里的【无底洞】,任何的 output 送去了【无底洞】就再也没了,一般情况下,要是你不想看到 output 或者output 太多太大了,有可能把硬碟给挤爆了的时候,程序的设计就会考虑把 output 送到 /dev/null 了



四、Android lunch产品分支构建

背景:南京广义软件有限公司英文名generalizesoft,所以此处使用generalize来命名总的产品目录。其中generalizex和genx则是对公司产品共性的描述。每套产品将差异性的部分定义在自己产品当中。再调用共性的部分,形成一个完整的产品。这样做日后需要维护所有产品的时候只需要维护一套代码,而不是每套产品有一套代码,修改的内容页不方便管理。

1. 目录结构:



2. 搭建步骤

1) 在device目录下创建一个名为generalize的文件夹







2) 对其中的文件分别进行编辑。generalize.mk 对应我们第一套产品的mk文件,这是差异性文件。不同的产品需要不同的mk文件。而generalizex.mk是无差异文件,意为产品的共性。所以在generalize.mk中定义如下,它需要引用generalizex.mk中定义的部分。这样做方便以后的产品差异性对比。同样的,BoardConfigCommon.mk定义了generalize一系列产品板文件的共性。在调用关系中可以看出它们之间的关系



3) Vendorsetup文件是定义lunch内容的文件。在其中加上我们产品。



4) 上述步骤处理完之后。运行envsetup.sh:

. build/envsetup.sh

之后再使用lunch,就可以看到基础效果图:






5) 编译android
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐