近日接手了后续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 (make就可以,这样写两个目的:1.多核编译 2.输出编译的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呢? 见这篇文章)
build/envsetup.sh脚本里做了哪些事情呢?
(1)遍历出device/xxx/xxx/里 所有的 envsetup.sh文件,并将他们都source进来,
这些 envsetup.sh里面有哪些代码呢
举一个例子 device/sprd/zsl1805/lava/envsetup.sh
add_lunch_combo zsl1805_lava-userdebug
add_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 fi
10 done
11 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 echo
100 check_hq_version
101 set_stuff_for_environment #设置环境变量
102 printconfig
103 }
另外,在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 。针对我们的例子TARGET_PRODUCT = zsl1805_lava TARGET_BUILD_VARIANT = user或者userdebug
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文件进行修改。