《Android的设计与实现:卷I》——第1章 1.3Android源码下载和编译

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所示。


《Android的设计与实现:卷I》——第1章 1.3Android源码下载和编译
《Android的设计与实现:卷I》——第1章 1.3Android源码下载和编译

注意 如果读者想进一步了解上述安装包的作用,可以到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。

上一篇:Springboot笔记~filter


下一篇:我们也说说Android.mk(4) - 依赖:目标编程的模式