1.3 Android源码下载和编译
Android源码的开发需要Linux环境,读者可以安装Ubuntu 10.04及其后续版本,推荐安装Ubuntu的10.04或者12.04这两个LTS(长期技术支持)版本。本书基于Jelly Bean(Android 4.1)分析源代码,该部分源代码的编译需要64位操作系统环境。本节将介绍如何在Ubuntu 12.04(LTS)-64bit上搭建Android源码开发所需环境,这是保障后续步骤能够顺利进行的前提条件,必须准确无误。Android源码分成上层系统源码和Linux Kernel两部分,需要分别下载。
注意 Android Jelly Bean源码超过6GB,编译至少需要25GB空间,应确保有足够的磁盘空间。
1.3.1 搭建开发环境
Android Jelly Bean的编译依赖Sun JDK 1.6,由于Ubuntu默认使用Open JDK,所以需要首先安装JDK 1.6。
步骤1 更新Ubuntu JDK软件源。在终端执行如下命令:
$ sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"
$ sudo apt-get update
步骤2 安装JDK 1.6。在终端执行如下命令:
$ sudo apt-get install sun-java6-jdk
步骤3 安装必需的开发包。在终端执行以下命令:
$ sudo apt-get install git-core gnupg flex bison gperf build-essential \
zip curl libc6-dev libncurses5-dev:i386 x11proto-core-dev \
libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-glx:i386 \
libgl1-mesa-dev g++-multilib mingw32 openjdk-6-jdk tofrodos \
python-markdown libxml2-utils xsltproc zlib1g-dev:i386
$ sudo ln -s /usr/lib/i386-linux-gnu/mesa/libGL.so.1 /usr/lib/i386-linux-gnu/libGL.so
注意 “”在Ubuntu 终端中会被解释成换行符。
各个安装包的主要作用如表1-5所示。
注意 如果读者想进一步了解上述安装包的作用,可以到Ubuntu网站(http://packages.ubuntu.com/)查询。
到此为止,源码开发环境已经准备完毕,可以开始下载源码了。
1.3.2 下载Android上层系统源代码
Android用repo和git管理源代码。
git是Linux之父 Linus Torvalds 为了管理 Linux 内核开发而开发的一个开放源码的分布式版本管理软件,它与 SVN、CVS这样的集中式版本管理软件有很大不同。在集中式版本管理软件中多个客户端共享一个仓库(repository),而在git这样的分布式版本管理软件中,每一个客户端都包含一个完整仓库,客户端可以离线操作,本地提交可以稍后再提交到服务器上。
Android是由kernel、dalvik、bionic、prebuilt、frameworks等多个git库组成,为了方便使用, Android项目提供了一个名为repo的Python的脚本来统一管理这些git仓库。
Android源代码分成两部分,其中Kernel部分需要单独下载。这里先讲解上层系统源码的下载流程。这部分代码非常庞大,下载时间需要数小时以上。
步骤1 建立repo工作目录。
$ mkdir ~/bin (在主目录下创建bin目录,~在Ubuntu下代表主目录)
$ PATH=~/bin:$PATH (将bin目录加入PATH环境变量)
步骤2 下载repo脚本。
$ curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo
(下载repo脚本到bin目录)
$ chmod a+x ~/bin/repo (给repo脚本可执行权限)
步骤3 建立Android源码目录。
$mkdir –p ~/android/jellybean (建立jellybean目录存放Android 4.1源代码)
$cd ~/android/jellybean (切换到jellybean目录下)
步骤4 初始化repo。
allong@android:~/android/jellybean $ repo init
-u https://android.googlesource.com/platform/manifest -b android-4.1.1_r3
其中,-u为源码的git服务器地址,-b为源码的某个分支。
如果读者不清楚源码服务器上的分支情况,可以执行“git ls-remote”命令查看远程服务器都有哪些分支,然后选择较新分支下载。命令如下:
allong@android:~/android/jellybean $ git ls-remote
–tags https://android.googlesource.com/platform/manifest
git ls-remote命令可以查看远程服务器上的branch列表。执行后显示内容如下:
1db98b3dedca5ab8b6eeefc5a7a98720e73fefdf refs/tags/android-2.3.7_r1
……
500aaaa87f49fddc1c7ca5066eebb2e03fdd14ac refs/tags/android-4.0.3_r1
……
可以通过tags后面的值判断有哪些branch可供下载。本书基于Android 4.1.1,所以传给-b参数的值取为“android-4.1.1_r3”。
注意 目录 refs 包含heads 和 tags两个子目录,其中存放了不同分支的头的索引,可以通过索引查看有哪些branch。如果没有指定-b,将下载Android主线(master默认分支)上最新版本的源代码,但这部分代码往往是不稳定的。
步骤5 下载Android源码。
初始化repo后,如果要下载Android源码,只需要进入源码根目录,然后在终端执行以下命令:
allong@android:~/android/jellybean$ repo sync
这个过程将从服务器同步源码,需要花费几个小时的时间。可以通过“repo sync –j8”命令开启并行下载,8是开启8线程。读者可根据主机情况自行调整参数。
1.3.3 下载指定模块源码
Android全部源码十分庞大,如果只需要下载部分源码,可以单独指定模块名称,这样可以节省大量时间。本节将介绍如何下载指定项目,步骤如下。
步骤1 查看都有哪些模块可以下载,在终端中执行以下命令:
allong@android:~/android/jellybean$ repo manifest -o -
执行后将显示如下信息:
…… (省略部分信息)
path="packages/wallpapers/HoloSpiral"/>
path="packages/wallpapers/LivePicker"/>
…… (省略部分信息)
……(省略部分信息)
其中,name表示项目模块的名称以及在源码服务器上的相对路径,path表示项目的本地路径。
注意 repo manifest -o - 命令读取的是本地源码根目录(笔者的本地源码目录是~/android/jellybean)下的.repo/manifests/default.xml文件,读者可以直接打开该文件,也可以得到同样的项目信息。
步骤2 将项目模块名指定给repo sync。
知道了有哪些项目模块可以单独下载,只需要将项目模块名指定给repo sync即可。例如,要下载platform/system/core项目,只需运行以下命令:
allong@android:~/android/jellybean$ repo sync platform/system/core
1.3.4 下载 Android Linux Kernel 源码
Kernel部分的源码没有采用repo工具管理,可以直接通过git工具下载,步骤如下。
步骤1 进入Android源码根目录。
笔者机器上的根目录是~/android/jellybean,建立kernel目录命令如下:
cd ~/android/jellybean
mkdir kernel
cd kernel
步骤2 下载Kernel源码。
读者可以在终端中执行以下任一条命令,下载Android Kernel部分源码。这里选择common.git通用版下载,其余是针对特定处理器的版本。
$ git clone https://android.googlesource.com/kernel/common.git
$ git clone https://android.googlesource.com/kernel/goldfish.git
$ git clone https://android.googlesource.com/kernel/msm.git
$ git clone https://android.googlesource.com/kernel/omap.git
$ git clone https://android.googlesource.com/kernel/samsung.git
$ git clone https://android.googlesource.com/kernel/tegra.git
步骤3 检出Kernel 3.0 分支。
由于Android Jelly Bean使用的是Linux 3.0内核,所以还需要切换到Kernel 3.0 分支。
$ cd common //进入common版内核的下载路径
$ git branch -a //查看都有哪些分支
$ git checkout remotes/origin/Android-3.0??//检出Kernel 3.0分支?
1.3.5 编译Android上层系统源码
一般来讲,源码下载后就可以直接学习Android源代码了。但这样无法调试源码,也无法得知源码编译后生成的文件是什么。所以这里继续讲解Android源代码的编译流程,步骤如下。
步骤1 导入预设脚本。在终端中执行以下命令:
allong@android:~/android/jellybean$ . build/envsetup.sh
注意 .后面有空格,“.”在Shell中是指令,使用方式是 “. filename”,作用是从filename中读取指令并执行。读者也可以用 “source build/envsetup.sh”代替,作用是一样的。
步骤2 指定产品名和编译变量。在终端中执行以下命令:
allong@android:~/android/jellybean$ lunch
You're building on Linux
Lunch menu... pick a combo:
1. full-eng
2. full_x86-eng
3. vbox_x86-eng
4. full_stingray-userdebug
……(省略部分内容)
Which would you like? [full-eng] 1 (输入1)
注意 lunch是envsetup.sh脚本中提供的函数,负责设置一些环境变量,比如TARGET_PRODUCT、TARGET_BUILD_VARIANT等。
full表示完全编译,eng表示工程版。full-eng对应模拟器设备。
步骤3 编译全部源码。在终端中执行以下命令:
allong@android:~/android/jellybean$ make -j8 (开启8线程开始编译)
编译全部源码十分耗时,但这也是必需的,只能等待。下一节将讲解如何编译指定模块。
1.3.6 编译指定模块源码
实际开发中,并不需要每次都编译所有源代码,只需要编译自己修改的模块即可。Android的编译系统提供了强大的机制支持单独模块的编译,而且十分简单。Android提供三种方式用于编译单独模块:
make 模块名
mm 来自于envsetup.sh脚本中注册的函数
mmm来自于envsetup.sh脚本中注册的函数
下面将分别介绍这三种方法。
1.make 模块名
这种方法适合第一次编译,会把依赖模块一并编译。它需要在全部源代码中找到编译模块的Android.mk文件,并检查依赖模块是否有修改,因此编译时间较长。使用这种方法,我们只需要搜索源码目录下的Android.mk文件,找到模块名,然后指定给make即可。
(1)编译应用层源码
对于应用层程序,需要查看Android.mk文件的LOCAL_PACKAGE_NAME变量。
例如,要编译Phone应用程序的源码,先查看Phone的Android.mk文件,在终端中运行以下命令:
allong@android:~/android/jellybean$ cat packages/apps/Phone/Android.mk
显示Android.mk的内容如下:
……(省略部分内容)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := com.android.phone.common
……(省略部分内容)
LOCAL_PACKAGE_NAME := Phone
……(省略部分内容)
找到LOCAL_PACKAGE_NAME字段,其值便是我们需要得到的编译参数,即Phone。得到编译参数后,在终端中运行如下命令便可单独编译Phone模块及其依赖模块:
allong@android:~/android/jellybean$make Phone
(2)编译框架层和系统运行库源码
对于框架层和系统运行库,需要查看LOCAL_MODULE变量。
以frameworks包中的源码为例,在终端中运行以下命令:
allong@android:~/android/jellybean$ find frameworks -name Android.mk
该命令将搜索frameworks目录下所有的Android.mk文件,列表如下:
frameworks/media/libvideoeditor/lvpp/Android.mk
frameworks/media/libvideoeditor/osal/src/Android.mk
frameworks/base/cmds/app_process/Android.mk
……(省略其他部分)
以app_process为例,在终端中运行以下命令:
allong@android:~/android/jellybean$ cat
frameworks/base/cmds/app_process/Android.mk
显示Android.mk的内容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= app_process
include $(BUILD_EXECUTABLE)
LOCAL_MODULE变量的值便是我们要找的模块名。在终端中运行以下命令:
allong@android:~/android/jellybean$make app_process
2.mmm命令
该命令是envsetup.sh中注册的函数,用于在源码根目录编译指定模块,参数为模块的相对路径。只能在第一次编译后使用。比如要编译Phone部分源码,需要在终端中执行以下命令:
allong@android:~/android/jellybean$mmm packages/apps/phone
3.mm命令
该命令也是envsetup.sh中注册的函数,用于在模块根目录编译这个模块。只能在第一次编译后使用。例如要编译Phone部分源码,需要在终端中执行以下命令:
allong@android:~/android/jellybean$cd packages/apps/phone
allong@android:~/android/jellybean/packages/apps/phone$mm
注意 mmm和mm命令必须在执行“.build/envsetup.sh”之后才能使用,并且只编译发生变化的文件。如果要编译模块的所有文件,需要-B选项,例如mm -B。