[Android] envsetup.sh及lunch是什么玩艺尔
2016-04-12 13:09
453 查看
在Android开发环境中编译一个目标时,一般要执行下面三行命令:
这三行命令是什么意思呢?下面逐一介绍。
上面两种执行方式的效果一样,都是在当前shell的子shell中进行的,类似于一个子进程,其结果是在脚本执行结束之后,脚本中的全局变量、函数等在当前shell是访问不到的,脚本的执行结果也不会影响当前shell,但是,这个脚本从名字上来看是做一些环境配置相关的事情,事实上也确实如此,这样的话脚本执行结果应该对当前shell有效,而句点命令就满足这个要求,另外,“source”命令也有同样的效果,可以替代句点命令。
以Android 6.0 marshmallow版本为例,这个脚本有1560行代码,里面都有些什么东西呢?这个脚本的一大特点就是定义了一系列函数,几乎占据了脚本的全部内容,这些函数可以在脚本内部调用,使用句点命令执行脚本之后,这些函数也可以在当前shell调用。下面先罗列一下这些函数的名字,多达70个,它们以空格分隔。
有这么多的函数,在shell中常用的也就几个,这些函数在脚本的第一个函数hmm中作了具体说明如下:
当我们执行这个脚本的时候,与这些函数都没什么关系,能够执行到的代码有下面几行。
首先,是一个if语句,判断当前shell即SHELL这个系统变量是否为bash,不是bash时给出警告:
然后,通过一个for循环查找device、vendor、product这几个目录下名为vendorsetup.sh的脚本,test命令测试被查找对象是否为一个目录,为目录时执行find命令开始查找,包括链接文件,目录查找深度为4,标准错误输出重定向到/dev/null,sort排序增加了下面echo命令的可读性,文件找到后立刻通过句点命令执行这个文件,for循环结束后unset变量f以防止全局变量的不良影响。
最后,执行addcompletions函数,如果shell不是bash或者bash的版本小于3的话将直接return,该函数的真正目的是执行sdk/adb_completion目录下所有以.bash结尾的脚本:
addcompletions函数的定义如下:
下面是envsetup.sh这个脚本执行的输出,显示了执行的外部脚本的路径:
以device/asus/deb/vendorsetup.sh为例,里面有一行内容如下:
上面的代码调用了add_lunch_combo函数,参数为aosp_deb-userdebug,意思是添加一个产品,产品名为aosp_deb,编译条件为userdebug,其实在envsetup.sh脚本内部也添加了几个默认的产品:
sdk/bash_completion/adb.bash定义了许多adb相关的函数,它们都以_adb开头,实现Tab键自动补全命令的功能,关键代码如下:
complete是bash的内建命令,-F表示执行adb命令时的自动补全功能由_adb函数提供,nospace表示自动补全命令的最后没有空格,默认是有空格的。
下面来看一下lunch函数的具体实现,它由两部分组成,第一部分是lunch函数本身,它的主要任务就是检查函数参数的正确性,即产品名是否正确、编译条件是否正确。首先,如果我们给lunch命令传入了参数它就开始检查这个参数,否则调用print_lunch_menu函数告诉我们lunch命令的参数有哪些候选项,通过read命令读入,这时我们可以输入某个具体的参数或者参数前面的数字序列号,两者都可以被正确处理。然后调用check_product检查产品名是否正确,调用check_variant检查编译条件是否正确。最后就是一些系统环境变量的设置,供makefile使用。
第二部分是_lunch函数,它的作用是实现lunch命令的候选参数的自动补全功能,通过Tab键完成,熟悉shell的话都知道通过Tab键实现命令自动补全,这个函数使用了一些相关的系统变量,如COMPREPLY、COMP_WORDS、COMP_CWORD,以及bash内建的命令completion和compgen,它们有其固定的用法。
下面是lunch命令后跟着回车键的结果:
然后选择对应的combo就可以了,比如说选择“5”或者“aosp_x86-eng”,两者的结果都是一样的,如下:
上面的结果与选择的“aosp_x86-eng”相匹配。
这个文件是必须的,一般不要修改,真正的东西在build/core/main.mk中。
有一点很重要,执行make命令时,真正的编译过程开始于envsetup.sh中的make函数,源码如下:
关键代码在于这一行:
执行get_make_command命令的返回值为“command make”,“$@”的意思是make命令后面的参数。make函数其它代码的作用就是根据编译结果计算整个编译过程所花的时间,编译结果的输出还有个简单的配色方案。
$ . build/envsetup.sh $ lunch <product_name>-<build_variant> $ make [module]
这三行命令是什么意思呢?下面逐一介绍。
1、envsetup.sh
envsetup.sh是个shell脚本,位于build目录下,第一行命令便是执行这个脚本。执行脚本有多种方式,那为什么要使用句点来执行呢?传统的执行脚本方式有下面两种:$ bash envsetup.sh $ chmod +x envsetup.sh; ./envsetup.sh
上面两种执行方式的效果一样,都是在当前shell的子shell中进行的,类似于一个子进程,其结果是在脚本执行结束之后,脚本中的全局变量、函数等在当前shell是访问不到的,脚本的执行结果也不会影响当前shell,但是,这个脚本从名字上来看是做一些环境配置相关的事情,事实上也确实如此,这样的话脚本执行结果应该对当前shell有效,而句点命令就满足这个要求,另外,“source”命令也有同样的效果,可以替代句点命令。
以Android 6.0 marshmallow版本为例,这个脚本有1560行代码,里面都有些什么东西呢?这个脚本的一大特点就是定义了一系列函数,几乎占据了脚本的全部内容,这些函数可以在脚本内部调用,使用句点命令执行脚本之后,这些函数也可以在当前shell调用。下面先罗列一下这些函数的名字,多达70个,它们以空格分隔。
addcompletions add_lunch_combo cgrep check_product check_variant choosecombo chooseproduct choosetype choosevariant core coredump_enable coredump_setup cproj croot findmakefile get_abs_build_var getbugreports get_build_var getdriver getlastscreenshot get_make_command getprebuilt getscreenshotpath getsdcardpath gettargetarch gettop ggrep godir hmm is isviewserverstarted jgrep key_back key_home key_menu lunch _lunch m make mangrep mgrep mm mma mmm mmma pez pid printconfig print_lunch_menu provision qpid rcgrep resgrep runhat runtest sepgrep set_java_home setpaths set_sequence_number set_stuff_for_environment settitle sgrep smoketest stacks startviewserver stopviewserver systemstack tapas tracedmdump treegrep
有这么多的函数,在shell中常用的也就几个,这些函数在脚本的第一个函数hmm中作了具体说明如下:
Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment: - lunch: lunch <product_name>-<build_variant> - tapas: tapas [<App1> <App2> ...] [arm|x86|mips|armv5|arm64|x86_64|mips64] [eng|userdebug|user] - croot: Changes directory to the top of the tree. - m: Makes from the top of the tree. - mm: Builds all of the modules in the current directory, but not their dependencies. - mmm: Builds all of the modules in the supplied directories, but not their dependencies. To limit the modules being built use the syntax: mmm dir/:target1,target2. - mma: Builds all of the modules in the current directory, and their dependencies. - mmma: Builds all of the modules in the supplied directories, and their dependencies. - provision: Flash device with all required partitions. Options will be passed on to fastboot. - cgrep: Greps on all local C/C++ files. - ggrep: Greps on all local Gradle files. - jgrep: Greps on all local Java files. - resgrep: Greps on all local res/*.xml files. - mangrep: Greps on all local AndroidManifest.xml files. - mgrep: Greps on all local Makefiles files. - sepgrep: Greps on all local sepolicy files. - sgrep: Greps on all local source files. - godir: Go to the directory containing a file.
当我们执行这个脚本的时候,与这些函数都没什么关系,能够执行到的代码有下面几行。
首先,是一个if语句,判断当前shell即SHELL这个系统变量是否为bash,不是bash时给出警告:
if [ "x$SHELL" != "x/bin/bash" ]; then case `ps -o command -p $$` in *bash*) ;; *) echo "WARNING: Only bash is supported, use of other shell would lead to erroneous results" ;; esac fi
然后,通过一个for循环查找device、vendor、product这几个目录下名为vendorsetup.sh的脚本,test命令测试被查找对象是否为一个目录,为目录时执行find命令开始查找,包括链接文件,目录查找深度为4,标准错误输出重定向到/dev/null,sort排序增加了下面echo命令的可读性,文件找到后立刻通过句点命令执行这个文件,for循环结束后unset变量f以防止全局变量的不良影响。
for f in `test -d device && find -L device -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \ `test -d vendor && find -L vendor -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \ `test -d product && find -L product -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` do echo "including $f" . $f done unset f
最后,执行addcompletions函数,如果shell不是bash或者bash的版本小于3的话将直接return,该函数的真正目的是执行sdk/adb_completion目录下所有以.bash结尾的脚本:
addcompletions
addcompletions函数的定义如下:
function addcompletions() { local T dir f if [ -z "${BASH_VERSION}" ]; then return fi if [ ${BASH_VERSINFO[0]} -lt 3 ]; then return fi dir="sdk/bash_completion" if [ -d ${dir} ]; then for f in `/bin/ls ${dir}/[a-z]*.bash 2> /dev/null`; do echo "including $f" . $f done fi }
下面是envsetup.sh这个脚本执行的输出,显示了执行的外部脚本的路径:
including device/asus/deb/vendorsetup.sh including device/asus/flo/vendorsetup.sh including device/asus/fugu/vendorsetup.sh including device/generic/mini-emulator-arm64/vendorsetup.sh including device/generic/mini-emulator-armv7-a-neon/vendorsetup.sh including device/generic/mini-emulator-mips/vendorsetup.sh including device/generic/mini-emulator-x86_64/vendorsetup.sh including device/generic/mini-emulator-x86/vendorsetup.sh including device/htc/flounder/vendorsetup.sh including device/huawei/angler/vendorsetup.sh including device/lge/bullhead/vendorsetup.sh including device/lge/hammerhead/vendorsetup.sh including device/moto/shamu/vendorsetup.sh including sdk/bash_completion/adb.bash
以device/asus/deb/vendorsetup.sh为例,里面有一行内容如下:
add_lunch_combo aosp_deb-userdebug
上面的代码调用了add_lunch_combo函数,参数为aosp_deb-userdebug,意思是添加一个产品,产品名为aosp_deb,编译条件为userdebug,其实在envsetup.sh脚本内部也添加了几个默认的产品:
add_lunch_combo aosp_arm-eng add_lunch_combo aosp_arm64-eng add_lunch_combo aosp_mips-eng add_lunch_combo aosp_mips64-eng add_lunch_combo aosp_x86-eng add_lunch_combo aosp_x86_64-eng
sdk/bash_completion/adb.bash定义了许多adb相关的函数,它们都以_adb开头,实现Tab键自动补全命令的功能,关键代码如下:
if [[ $(type -t compopt) = "builtin" ]]; then complete -F _adb adb else complete -o nospace -F _adb adb fi
complete是bash的内建命令,-F表示执行adb命令时的自动补全功能由_adb函数提供,nospace表示自动补全命令的最后没有空格,默认是有空格的。
2、lunch
lunch是envsheup.sh中定义的一个函数,参数是前面介绍的通过add_lunch_combo函数添加的某个值,由两部分组成,产品名和编译条件,后者包括eng、userdebug、user,这三个选项在envsetup.sh中保存在了一个数组(或列表)中:VARIANT_CHOICES=(user userdebug eng)
下面来看一下lunch函数的具体实现,它由两部分组成,第一部分是lunch函数本身,它的主要任务就是检查函数参数的正确性,即产品名是否正确、编译条件是否正确。首先,如果我们给lunch命令传入了参数它就开始检查这个参数,否则调用print_lunch_menu函数告诉我们lunch命令的参数有哪些候选项,通过read命令读入,这时我们可以输入某个具体的参数或者参数前面的数字序列号,两者都可以被正确处理。然后调用check_product检查产品名是否正确,调用check_variant检查编译条件是否正确。最后就是一些系统环境变量的设置,供makefile使用。
function lunch() { local answer if [ "$1" ] ; then answer=$1 else print_lunch_menu echo -n "Which would you like? [aosp_arm-eng] " read answer fi local selection= if [ -z "$answer" ] then selection=aosp_arm-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 }
第二部分是_lunch函数,它的作用是实现lunch命令的候选参数的自动补全功能,通过Tab键完成,熟悉shell的话都知道通过Tab键实现命令自动补全,这个函数使用了一些相关的系统变量,如COMPREPLY、COMP_WORDS、COMP_CWORD,以及bash内建的命令completion和compgen,它们有其固定的用法。
function _lunch() { local cur prev opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" COMPREPLY=( $(compgen -W "${LUNCH_MENU_CHOICES[*]}" -- ${cur}) ) return 0 } complete -F _lunch lunch
下面是lunch命令后跟着回车键的结果:
You're building on Linux Lunch menu... pick a combo: 1. aosp_arm-eng 2. aosp_arm64-eng 3. aosp_mips-eng 4. aosp_mips64-eng 5. aosp_x86-eng 6. aosp_x86_64-eng 7. aosp_deb-userdebug 8. aosp_flo-userdebug 9. full_fugu-userdebug 10. aosp_fugu-userdebug 11. mini_emulator_arm64-userdebug 12. m_e_arm-userdebug 13. mini_emulator_mips-userdebug 14. mini_emulator_x86_64-userdebug 15. mini_emulator_x86-userdebug 16. aosp_flounder-userdebug 17. aosp_angler-userdebug 18. aosp_bullhead-userdebug 19. aosp_hammerhead-userdebug 20. aosp_hammerhead_fp-userdebug 21. aosp_shamu-userdebug Which would you like? [aosp_arm-eng]
然后选择对应的combo就可以了,比如说选择“5”或者“aosp_x86-eng”,两者的结果都是一样的,如下:
============================================ PLATFORM_VERSION_CODENAME=REL PLATFORM_VERSION=6.0.1 TARGET_PRODUCT=aosp_x86 TARGET_BUILD_VARIANT=eng TARGET_BUILD_TYPE=release TARGET_BUILD_APPS= TARGET_ARCH=x86 TARGET_ARCH_VARIANT=x86 TARGET_CPU_VARIANT= TARGET_2ND_ARCH= TARGET_2ND_ARCH_VARIANT= TARGET_2ND_CPU_VARIANT= HOST_ARCH=x86_64 HOST_OS=linux HOST_OS_EXTRA=Linux-3.13.0-74-generic-x86_64-with-Ubuntu-14.04-trusty HOST_CROSS_OS=windows HOST_BUILD_TYPE=release BUILD_ID=MASTER OUT_DIR=out ============================================
上面的结果与选择的“aosp_x86-eng”相匹配。
3、make
make很简单了,就是执行makefile,Android源码根目录下只有一个简单的Makefile,只读文件:### DO NOT EDIT THIS FILE ###a include build/core/main.mk ### DO NOT EDIT THIS FILE ###
这个文件是必须的,一般不要修改,真正的东西在build/core/main.mk中。
有一点很重要,执行make命令时,真正的编译过程开始于envsetup.sh中的make函数,源码如下:
function make() { local start_time=$(date +"%s") $(get_make_command) "$@" local ret=$? local end_time=$(date +"%s") local tdiff=$(($end_time-$start_time)) local hours=$(($tdiff / 3600 )) local mins=$((($tdiff % 3600) / 60)) local secs=$(($tdiff % 60)) local ncolors=$(tput colors 2>/dev/null) if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then color_failed=$'\E'"[0;31m" color_success=$'\E'"[0;32m" color_reset=$'\E'"[00m" else color_failed="" color_success="" color_reset="" fi echo if [ $ret -eq 0 ] ; then echo -n "${color_success}#### make completed successfully " else echo -n "${color_failed}#### make failed to build some targets " fi if [ $hours -gt 0 ] ; then printf "(%02g:%02g:%02g (hh:mm:ss))" $hours $mins $secs elif [ $mins -gt 0 ] ; then printf "(%02g:%02g (mm:ss))" $mins $secs elif [ $secs -gt 0 ] ; then printf "(%s seconds)" $secs fi echo " ####${color_reset}" echo return $ret }
关键代码在于这一行:
$(get_make_command) "$@"
执行get_make_command命令的返回值为“command make”,“$@”的意思是make命令后面的参数。make函数其它代码的作用就是根据编译结果计算整个编译过程所花的时间,编译结果的输出还有个简单的配色方案。
相关文章推荐
- 别再抱怨了,国内这么多优秀的Android资源你都知道吗?(转)
- Android Studio 入门(转)
- android RelativeLayout 内容居中解决办法
- 关于新建Android Studio项目时默认的编译sdk版本导致的兼容问题
- Android RelativeLayout各个属性的含义
- Android Studio2.0 安卓开发者不得不知的功能
- android开发 华为 点击跳转到权限管理页面
- Android_实现星星控件_学习
- Android之去掉ListView的点击背景
- Android 自定义ViewGroup
- Android M MO SMS/MMS FLOW
- Android开发之Mac adb配置
- Android动态加载Dex机制解析
- 在android中进行视频的分割
- React-Native系列Android——Native与Javascript通信原理(一)
- android 解析、生成二维码
- android Activity之间数据传递 Bitmap
- android 音频视频合并
- android 测试开发概述
- Android中的线程池和AsyncTask异步任务(二)