在Android源码下载完成后,只需要简单的三个步骤就能把Android编译完成( http://source.android.com/source/building.html):
1.当前目录切换到Android源码下,

. build/envsetup.sh

2.执行lunch命令,选择你要的target


3.make

这些步骤的确很简单,但是背后实际做了什么呢?这篇文章会分析envsetup.sh文件,对shell不熟悉的同学可以打开(http://www.gnu.org/software/bash/manual/bash.html),遇到不懂的地方可以参考一下。

首先我们看envsetup.sh,里面声明了很多方法,但是我们用户一般就使用lunch就够了,所以不一一讲解,不然会昏了。在执行完这个文件后,这些方法都可以在当前的shell里使用,lunch便是其中的一个方法,所以我们必须先执行envsetup.sh,才能使用lunch。我们看看除了方法的声明外,它还做了什么。

77行:

VARIANT_CHOICES=(user userdebug eng)



这里定义了一个名为VARIANT_CHOICES的数组,它是作用是让用户选择不同的编译方案,user适合正式的产品发布,userdebug在user基础上提供root和调试工具,eng适合开发,附带调试工具。

191-257行:

case `uname -s` in
    Linux)
        function choosesim()
        {
            echo "Build for the simulator or the device?"
            echo "     1. Device"
            echo "     2. Simulator"
            echo

            export TARGET_SIMULATOR=
            local ANSWER
            while [ -z $TARGET_SIMULATOR ]
            do
                echo -n "Which would you like? [1] "
                if [ -z "$1" ] ; then
                    read ANSWER
                else
                    echo $1
                    ANSWER=$1
                fi
                case $ANSWER in
                "")
                    export TARGET_SIMULATOR=false
                    ;;
                1)
                    export TARGET_SIMULATOR=false
                    ;;
                Device)
                    export TARGET_SIMULATOR=false
                    ;;
                2)
                    export TARGET_SIMULATOR=true
                    ;;
                Simulator)
                    export TARGET_SIMULATOR=true
                    ;;
                *)
                    echo
                    echo "I didn't understand your response.  Please try again."
                    echo
                    ;;
                esac
                if [ -n "$1" ] ; then
                    break
                fi
            done

            set_stuff_for_environment
        }
        ;;
    *)
        function choosesim()
        {
            echo "Only device builds are supported for" `uname -s`
            echo "     Forcing TARGET_SIMULATOR=false"
            echo
            if [ -z "$1" ]
            then
                echo -n "Press enter: "
                read
            fi

            export TARGET_SIMULATOR=false
            set_stuff_for_environment
        }
        ;;
esac

这是shell的一个case,其实就像我们平时c,java里面的switch。`uname -s`返回的是内核的名字,如果你使用的是基于Linux内核的系统,如Ubuntu,Fedora,OpenSuse等,此时则返回Linux,使用Mac的就返回Darwin。这个case是根据Linux内核和其他内核定义不同choosesim方法。

434行:

unset LUNCH_MENU_CHOICES

清除LUNCH_MENU_CHOICES变量的值


448-449行:

add_lunch_combo full-eng add_lunch_combo full_x86-eng
这2行代码的调用了add_lunch_combo方法

function add_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数组增加内容。


453-455行:



if [ "$(uname)" = "Linux" ] ; then add_lunch_combo simulator fi再次调用uname,获得内核名字,如是Linux则添加simulator的选项。



528行:alias bib=breakfast

设定一个别名,以后调用bib就等于调用breakfast。


924-938行:

case `uname -s` in
    Darwin)
        function sgrep()
        {
            find -E . -type f -iregex '.*\.(c|h|cpp|S|java|xml|sh|mk)' -print0 | xargs -0 grep --color -n "$@"
        }

        ;;
    *)
        function sgrep()
        {
            find . -type f -iregex '.*\.\(c\|h\|cpp\|S\|java\|xml\|sh\|mk\)' -print0 | xargs -0 grep --color -n "$@"
        }
        ;;
esac 
 
 又一次调用了uname,根据Mac和其他系统的find命令不同,声明了其对应的sgrep方法。



955-980行:

case `uname -s` in
    Darwin)
        function mgrep()
        {
            find -E . -type f -iregex '.*/(Makefile|Makefile\..*|.*\.make|.*\.mak|.*\.mk)' -print0 | xargs -0 grep --color -n "$@"
        }

        function treegrep()
        {
            find -E . -type f -iregex '.*\.(c|h|cpp|S|java|xml)' -print0 | xargs -0 grep --color -n -i "$@"
        }

        ;;
    *)
        function mgrep()
        {
            find . -regextype posix-egrep -iregex '(.*\/Makefile|.*\/Makefile\..*|.*\.make|.*\.mak|.*\.mk)' -type f -print0 | xargs -0 grep --color -n "$@"
        }

        function treegrep()
        {
            find . -regextype posix-egrep -iregex '.*\.(c|h|cpp|S|java|xml)' -type f -print0 | xargs -0 grep --color -n -i "$@"
        }

        ;;
esac 
 
 跟上者一样。



1590-1597行:


_xarray=(a b c) if [ -z "${_xarray[${#_xarray[@]}]}" ] then _arrayoffset=1 #zero-based (bash) else _arrayoffset=0 #one-based (zsh) fi unset _xarray

这里是要测试当前shell的数组第一个元素的index是从1还是0开始。这里有点复杂,有的同学可能没看懂,那我就慢慢解释一下了,首先

${#_xarray[@]}


是获取_xarry数组的元素个数,这里返回3。然后

${_xarray[${#_xarray[@]}]}


就是获取_xarray中index为3的元素,如果使用的bash,这里返回空,因为bash是zero-based,如果是zsh,则返回 'c' ,因为zsh是one-based。如果返回的是空,也就是当前使用bash,则设置_arrayoffset = 1 , 否则设置为0.

这里一开始不太明白,因为bash是zero-based的,不是应该设置_arrayoffset = 0 吗?怎么倒过来了,想了好久,后来看了下它的具体使用情况,如下篇文章会说到的lunch命令,直接执行lunch命令时,会列出各种combo:


You're building on Linux Lunch menu... pick a combo: 1. full-eng 2. full_x86-eng 3. simulator 4. generic_sholest-userdebug 5. cyanogen_ace-eng 6. cyanogen_anzu-eng 7. cyanogen_blade-eng 8. cyanogen_bravo-eng 9. cyanogen_bravoc-eng 10. cyanogen_buzz-eng 11. cyanogen_c660-eng 12. cyanogen_click-eng 13. cyanogen_captivatemtd-eng 14. cyanogen_coconut-eng 15. cyanogen_cooper-eng 16. cyanogen_crespo-eng 17. cyanogen_crespo4g-eng 18. cyanogen_desirec-eng 19. cyanogen_dream_sapphire-eng 20. cyanogen_droid2-eng 21. cyanogen_droid2we-eng 22. cyanogen_e510-eng 23. cyanogen_e730-eng 24. cyanogen_encore-eng 25. cyanogen_epicmtd-eng 26. cyanogen_es209ra-eng 27. cyanogen_espresso-eng 28. cyanogen_fascinatemtd-eng 29. cyanogen_galaxys2-eng 30. cyanogen_galaxys2att-eng 31. cyanogen_galaxysbmtd-eng 32. cyanogen_galaxysmtd-eng 33. cyanogen_generic-eng 34. cyanogen_glacier-eng 35. cyanogen_hallon-eng 36. cyanogen_hero-eng 37. cyanogen_heroc-eng 38. cyanogen_inc-eng 39. cyanogen_iyokan-eng 40. cyanogen_jordan-eng 41. cyanogen_legend-eng 42. cyanogen_leo-eng 43. cyanogen_liberty-eng 44. cyanogen_mango-eng 45. cyanogen_mecha-eng 46. cyanogen_mesmerizemtd-eng 47. cyanogen_mimmi-eng 48. cyanogen_morrison-eng 49. cyanogen_motus-eng 50. cyanogen_one-eng 51. cyanogen_olympus-eng 52. cyanogen_p500-eng 53. cyanogen_p920-eng 54. cyanogen_p925-eng 55. cyanogen_p970-eng 56. cyanogen_p990-eng 57. cyanogen_p999-eng 58. cyanogen_passion-eng 59. cyanogen_robyn-eng 60. cyanogen_saga-eng 61. cyanogen_satsuma-eng 62. cyanogen_shadow-eng 63. cyanogen_shakira-eng 64. cyanogen_sholes-eng 65. cyanogen_showcasemtd-eng 66. cyanogen_smb_a1002-eng 67. cyanogen_smb_a1004-eng 68. cyanogen_smb_a1011-eng 69. cyanogen_smb_b9701-eng 70. cyanogen_smultron-eng 71. cyanogen_speedy-eng 72. cyanogen_supersonic-eng 73. cyanogen_tass-eng 74. cyanogen_u8150-eng 75. cyanogen_u8220-eng 76. cyanogen_urushi-eng 77. cyanogen_v9-eng 78. cyanogen_vega-eng 79. cyanogen_vision-eng 80. cyanogen_vivo-eng 81. cyanogen_vivow-eng 82. cyanogen_z71-eng 83. cyanogen_zeppelin-eng 84. cyanogen_zero-eng 85. cyanogen_zeus-eng 86. cyanogen_zeusc-eng 87. cyanogen_sholest-eng Which would you like? [full-eng]
可以看到是从序号1开始递增的,这时用户是输入序号或者相应的字符串以选择想要的combo,如果用户使用了序号,看看lunch中的处理:



selection=${LUNCH_MENU_CHOICES[$(($answer-$_arrayoffset))]}
answer变量是用户输入的数值,如我输入了87,要获得相应的字符串,则必须减去_arrayoffset,就是减去1,也就解释了为什么当使用bash时要设_arrayoffset为1了!


1600-1605行:


for f in `{ setopt nullglob; /bin/ls vendor/*/vendorsetup.sh vendor/*/build/vendorsetup.sh device/*/*/vendorsetup.sh; } 2> /dev/null` do echo "including $f" . $f done unset f
setopt是zsh的东西,我使用bash就没管它了。这里使用ls列出了vendor下指定位置的vendorsetup.sh,把文件列表传给for循环,


echo打印出include了哪个文件,并且执行了它。

其实vendorsetup.sh的内容就是一堆add_lunch_combo,到此为止,我们的envsetup.sh已经执行完了,其实它的作用主要是2点:

1.声明了之后会使用的各种方法和变量。

2.设定好lunch命令需要使用的combos。