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

Android编译过程详解(一)

2016-04-15 12:47 309 查看

Android编译过程详解(一)

注:本文转载自Android编译过程详解(一):/article/5989351.html

Android的优势就在于其开源,软件开发商可以根据自己的需求来基于Android定制自己的OS,如小米(MIUI),魅族(Flyme),锤子(Smartisan)等,因此,在对Android的源码进行定制的时候,有必要了解Android的编译过程。
Android的官方网站:http://source.android.com/source/building.html,给出了一个通用系统的编译过程
+--------------------------------------------------------------------------------------------------------------------+
本文使用Android版本为2.1
+--------------------------------------------------------------------------------------------------------------------+

首先来看一下google给出的编译步骤:
1> source build/envsetup.sh:加载命令
2> lunch:选择平台编译选项
3> make:执行编译
我们按照编译步骤来分析编译过程的细节,最终添加自己的平台编译选项。
1. source build/envsetup.sh
这个命令是用来将envsetup.sh里的所有用到的命令加载到环境变量里去,我们来分析下它。
envsetup.sh里的主要命令如下:

function help()                  
# 显示帮助信息

function get_abs_build_var()            # 获取绝对变量

function get_build_var()              # 获取绝对变量

function check_product()              # 检查product

function check_variant()              # 检查变量

function setpaths()                # 设置文件路径

function printconfig()               # 打印配置

function set_stuff_for_environment()       # 设置环境变量

function set_sequence_number()         # 设置序号

function settitle()                # 设置标题

function choosetype()               # 设置type

function chooseproduct()             # 设置product

function choosevariant()             # 设置variant

function tapas()                 # 功能同choosecombo

function choosecombo()              # 设置编译参数

function add_lunch_combo()            # 添加lunch项目

function print_lunch_menu()            # 打印lunch列表

function lunch()                  # 配置lunch

function m()                    # make from top

function findmakefile()              # 查找makefile

function mm()                   # make from current directory

function mmm()                  # make the supplied directories

function croot()                  # 回到根目录

function cproj()

function pid()

function systemstack()

function gdbclient()

function jgrep()                  # 查找java文件

function cgrep()                 # 查找c/cpp文件

function resgrep()

function tracedmdump()

function runhat()

function getbugreports()

function startviewserver()

function stopviewserver()

function isviewserverstarted()

function smoketest()

function runtest()

function godir ()                  # 跳到指定目录 405
# add_lunch_combo函数被多次调用,就是它来添加Android编译选项

# Clear this variable. It will be built up again when the vendorsetup.sh

406 # files are included at the end of this file.

# 清空LUNCH_MENU_CHOICES变量,用来存在编译选项

407 unset LUNCH_MENU_CHOICES

408 function add_lunch_combo()

409 {

410 local new_combo=$1 # 获得add_lunch_combo被调用时的参数

411 local c

# 依次遍历LUNCH_MENU_CHOICES里的值,其实该函数第一次调用时,该值为空

412 for c in ${LUNCH_MENU_CHOICES[@]} ; do

413 if [ "$new_combo" = "$c" ] ; then # 如果参数里的值已经存在于LUNCH_MENU_CHOICES变量里,则返回

414 return

415 fi

416 done

# 如果参数的值不存在,则添加到LUNCH_MENU_CHOICES变量里

417 LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)

418 }

# 这是系统自动增加了一个默认的编译项 generic-eng

420 # add the default one here

421 add_lunch_combo generic-eng # 调用上面的add_lunch_combo函数,将generic-eng作为参数传递过去

422

423 # if we're on linux, add the simulator. There is a special case

424 # in lunch to deal with the simulator

425 if [ "$(uname)" = "Linux" ] ; then

426 add_lunch_combo simulator

427 fi
# 下面的代码很重要,它要从vendor目录下查找vendorsetup.sh文件,如果查到了,就加载它

1037 # Execute the contents of any vendorsetup.sh files we can find.

1038 for f in `/bin/ls vendor/*/vendorsetup.sh vendor/*/build/vendorsetup.sh 2> /dev/null`

1039 do

1040 echo "including $f"

1041 . $f # 执行找到的脚本,其实里面就是厂商自己定义的编译选项

1042 done

1043 unset f

envsetup.sh其主要作用如下:
  1. 加载了编译时使用到的函数命令,如:help,lunch,m,mm,mmm等

  2. 添加了两个编译选项:generic-eng和simulator,这两个选项是系统默认选项

  3. 查找vendor/<-厂商目录>/和vendor/<厂商目录>/build/目录下的vendorsetup.sh,如果存在的话,加载执行它,添加厂商自己定义产品的编译选项

其实,上述第3条是向编译系统添加了厂商自己定义产品的编译选项,里面的代码就是:add_lunch_combo xxx-xxx。
根据上面的内容,可以推测出,如果要想定义自己的产品编译项,简单的办法是直接在envsetup.sh最后,添加上add_lunch_combo myProduct-eng,当然这么做,不太符合上面代码最后的本意,我们还是老实的在vendor目录下创建自己公司名字,然后在公司目录下创建一个新的vendorsetup.sh,在里面添加上自己的产品编译项

这样,当我们在执行source build/envsetup.sh命令的时候,可以在shell上看到下面的信息:

2. 按照android官网的步骤,开始执行lunch full-eng
当然如果你按上述命令执行,它编译的还是通用的eng版本系统,不是我们个性系统,我们可以执行lunch命令,它会打印出一个选择菜单,列出可用的编译选项
如果你按照第一步中添加了vendorsetup.sh那么,你的选项中会出现:

其中第3项是我们自己添加的编译项。
lunch命令是envsetup.sh里定义的一个命令,用来让用户选择编译项,来定义Product和编译过程中用到的全局变量。
我们一直没有说明前面的fs100-eng是什么意思,现在来说明下,fs100是我定义的产品的名字,eng是产品的编译类型,除了eng外,还有user, userdebug,分别表示:
eng: 工程机,
user:最终用户机
userdebug:调试测试机
tests:测试机
由此可见,除了eng和user外,另外两个一般不能交给最终用户的。
那么这四个类型是干什么用的呢?其实,在main.mk里有说明,在Android的源码里,每一个目标(也可以看成工程)目录都有一个Android.mk的makefile,每个目标的Android.mk中有一个类型声明:LOCAL_MODULE_TAGS,这个TAGS就是用来指定,当前的目标编译完了属于哪个分类里。

PS:Android.mk和Linux里的makefile不太一样,它是Android编译系统自己定义的一个makefile来方便编译成:c,c++的动态、静态库或可执行程序,或java库或android的程序,
好了,我们来分析下lunch命令干了什么?
function lunch()

{

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]*$")

then

# 如果answer是选择菜单的数字,则获取该数字对应的字符串

if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]

then

selection=${LUNCH_MENU_CHOICES[$(($answer-$_arrayoffset))]}

fi

# 如果 answer字符串匹配 *-*模式(*的开头不能为-)

elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")

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/-.*$//")
# 检查之,调用关系 check_product()->get_build_var()->build/core/config.mk比较罗嗦,不展开了

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
# 将 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
echo
# 设置到环境变量,比较多,不再一一列出,最简单的方法 set >env.txt 可获得

set_stuff_for_environment

# 打印一些主要的变量, 调用关系 printconfig()->get_build_var()->build/core/config.mk->build/core/envsetup.mk 比较罗嗦,不展开了

printconfig

}

由上面分析可知,lunch命令可以带参数和不带参数,最终导出一些重要的环境变量,从而影响编译系统的编译结果。导出的变量如下(以实际运行情况为例)

执行完上述两个步骤,就该执行:make命令了,下篇来分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: