你的位置:首页 > 操作系统

[操作系统]android编译系统学习


近日接手了后续android新平台项目搭建的任务。

本文内容基于sprd公司提供的android5.1源码。

一、一般的编译工作流程

我们代码一般情况下是从芯片商SPRD/MTK获得的。

源码的编译上,一般还是和google官网(http://source.android.com/source/building.html)上要求的一致。分为三步:

1.source build/envsetup.sh

2.lunch xxx

3.make -j8 2>&1 |tee build.log 

4.mmm "模块"可以编译单独的模块(要全编译后才可以编译模块)

 

二、原理

android的编译是基于的linux的make命令的。Make命令在执行的时候,默认会在当前目录找到一个Makefile文件,然后根据Makefile文件中的指令来对代码进行编译。

Makefile文件最基础的功能就是描述文件之间的依赖关系,以及怎么处理这些依赖关系。

整个android源码的编译就是由根目录的Makefile文件来执行的。

现在我们就了解下我们编译过程中的命令做了哪些事情。

1.source build/envsetup.sh

执行build/envsetup.sh脚本(ps: source xx.sh命令就是 执行一个脚本的意思。那为什么不直接./xxx.sh呢? 见这篇文章http://blog.csdn.net/coofive/article/details/671835)

build/envsetup.sh脚本里做了哪些事情呢?

(1)遍历出device/xxx/xxx/里 所有的 envsetup.sh文件,并将他们都source进来,

这些 envsetup.sh里面有哪些代码呢

举一个例子 device/sprd/zsl1805/lava/envsetup.sh

add_lunch_combo zsl1805_lava-userdebugadd_lunch_combo zsl1805_lava-user

add_lunch_combo是什么东西?我们先不管,往下看。

(2)定义各种函数。

如:lunch mmm add_lunch_combo等。

lunch:用来初始化编译环境,例如设置环境变量和指定目标产品型号。后面再说

m:相当于是在执行make命令。对整个Android源码进行编译。

mm:如果是在Android源码根目录下执行,那么就相当于是执行make命令对整个源码进行编译。如果是在Android源码根目录下的某一个子目录执行,那么就在会在从该子目录开始,一直往上一个目录直至到根目录,寻找是否存在一个Android.mk文件。如果存在的话,那么就通过make命令对该Android.mk文件描述的模块进行编译。

mmm:后面可以跟一个或者若干个目录。就是编译模块了。如mmm "packages/apps/Launcher3"

add_lunch_combo:

 1 unset LUNCH_MENU_CHOICES 2 function add_lunch_combo() 3 { 4   local new_combo=$1 5   local c 6   for c in ${LUNCH_MENU_CHOICES[@]} ; do 7     if [ "$new_combo" = "$c" ] ; then 8       return 9     fi10   done11   LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)12 }

这里我们只要知道add_lunch_combo把"zsl1805_lava_userdebug"和"zsl1805_lava_user"add到"LUNCH_MENU_CHOICES"里了。

到这里,source build/envsetup.sh的工作就做完了。

此时可以在shell命令行里看到 

including device/sprd/zsl1805/lava/vendorsetup.sh的字样,这个文件确实被加载进来了

 

 2.lunch操作

我们先看下build/envsetup.sh中lunch函数的代码

 1 #打印"LUNCH_MENU_CHOICES"中所有值 2 function print_lunch_menu() 3 { 4   local uname=$(uname) 5   echo 6   echo "You're building on" $uname 7   echo 8   echo "Lunch menu... pick a combo:" 9  10   local i=1 11   local choice 12   for choice in ${LUNCH_MENU_CHOICES[@]} 13   do 14     echo "   $i. $choice" 15     i=$(($i+1)) 16   done 17  18   echo 19 } 20  21 function lunch() 22 { 23   local answer 24 #如果只输入了lunch命令,会打印"LUNCH_MENU_CHOICES"中所有值 25   if [ "$1" ] ; then 26     answer=$1 27   else 28     print_lunch_menu 29     echo -n "Which would you like? [aosp_arm-eng] " 30     read answer 31   fi 32  33 #判断lunch后面的值是不是LUNCH_MENU_CHOICES中的值,如果是,则把这个值赋给selection 34   local selection= 35  36   if [ -z "$answer" ] 37   then 38     selection=aosp_arm-eng 39   elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$") 40   then 41     if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ] 42     then 43       selection=${LUNCH_MENU_CHOICES[$(($answer-1))]} 44     fi 45   elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$") 46   then 47     selection=$answer 48   fi 49  50   if [ -z "$selection" ] 51   then 52     echo 53     echo "Invalid lunch combo: $answer" 54     return 1 55   fi 56  57   export TARGET_BUILD_APPS= 58   export CONFIG_TARGET_BUILD_TYPE= 59  60  61 #对selection值进行分割操作,得到product和variant 62  63   local product=$(echo -n $selection | sed -e "s/-.*$//") 64   check_product $product 65   if [ $? -ne 0 ] 66   then 67     echo 68     echo "** Don't have a product spec for: '$product'" 69     echo "** Do you have the right repo manifest?" 70     product= 71   fi 72  73   local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//") 74   check_variant $variant 75   if [ $? -ne 0 ] 76   then 77     echo 78     echo "** Invalid variant: '$variant'" 79     echo "** Must be one of ${VARIANT_CHOICES[@]}" 80     echo "** Vinton.Wang -----------> variant:'$variant'" 81     export CONFIG_TARGET_BUILD_TYPE=$(echo $variant | tr '[a-z]' '[A-Z]') 82     echo "** Vinton.Wang -----------> type:'$CONFIG_TARGET_BUILD_TYPE'" 83     variant="userdebug" 84   fi 85  86   if [ -z "$product" -o -z "$variant" ] 87   then 88     echo 89     return 1 90   fi 91 #得到TARGET_PRODUCT TARGET_BUILD_VARIANT TARGET_BUILD_TYPE三个值 92   export TARGET_PRODUCT=$product 93   export TARGET_BUILD_VARIANT=$variant 94   export TARGET_BUILD_TYPE=release 95   export ARCH=$(gettargetarch) 96   echo "############# ${ARCH} ##############" 97  98   echo 99   echo100   check_hq_version101   set_stuff_for_environment #设置环境变量102   printconfig103 }

 

 另外,在check_product()和printconfig()两个方法中,调用了get_build_var()函数。

# Get the exact value of a build variable.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)}

也就是说,在lunch的过程中,编译了build/core/config.mk

 

总结一下Lunch流程:

1.判断是不是没有参数

2.判断参数是否合法

3.将参数进行分割操作,得到TARGET_PRODUCT    TARGET_BUILD_VARIANT 

4.设置环境变量

 

如果我们直接输入lunch,会有下面的界面出现

You're building on Linux

 
generic-eng simulator fs100-eng
Lunch menu... pick a combo:
     1. generic-eng
     2. simulator
     3. fs100-eng
         .....................

        40. zsl1805_lava-userdebug
        41. zsl1805_lava-user

 

然后,我们需要lunch zsl1805_lava-userdebug即可。

到此时,android编译环境的初始化就完成了。

贴一张初始化完成之后,我们获得的东西

 

3.make

下面就是执行编译了。根目录里的Makefile文件中是这样的

### DO NOT EDIT THIS FILE ###include build/core/main.mk### DO NOT EDIT THIS FILE ###

Make其实就是按照main.mk的编译规则来编译的。

整个编译过程还是比较复杂的,这里有一张main.mk的编译示意图,基本涵盖了所有编译内容。这里就不去深入研究了,如果对某些细节感兴趣,可以看下面的参考资料再去深入学习。

我们只要知道,如果我们要对编译过程中的流程修改,就按照这个图找到相应的.mk文件进行修改。

 

 

参考资料:

Android编译系统环境初始化过程分析

Android编译过程详解(一):http://www.cnblogs.com/mr-raptor/archive/2012/06/07/2540359.html

Android编译过程详解(二):http://www.cnblogs.com/mr-raptor/archive/2012/06/08/2541571.html