第4章 源代码的下载和编译
上一章介绍了Git的使用方法。在本章玩点真格的。使用Git下载两套源代码。这两套源代码是本书的重点。其中一套就是Android源代码。这套源代码可谓是重量级别,全部下载有近4GB的大小。前面曾提到的HAL(硬件抽象层)的代码就位于这4GB的某个位置(后面会详细介绍)。不过很遗憾,Android的核心部分,也就是Linux内核源代码可不在这4GB之中。可能有的读者会认为到www.kernel.org下载一个Linux内核就可以了。我的回答是:NO。Android所使用的Linux内核是在官方Linux内核的基础上修改而成的。虽然大部分与官方Linux内核兼容,但仍然不能简单地互换。所以要用Google提供的URI去下载适应于Android的Linux内核。Linux内核源代码与Android源代码比起来可谓是小巫见大巫。完全下载Linux内核源代码只有几百MB(解压后)。本章主要介绍了如何下载和编译Android源代码和Linux内核源代码。现在就让我们开始源代码之旅吧!
4.1 下载、编译和测试Android源代码
Android源代码包含了很多东西,如内嵌在Android系统中的应用程序(拍照、计算器、日历、相册、拨号器等)的源代码;Android SDK带的各种工具(如adb、emulator、mksdcard、aapt等)的源代码;Android NDK的源代码;当然,还有本书要详细介绍的HAL源代码。所以从Android所带的源代码数量来看,Android源代码还是很复杂的。不过也不用担心。研究Android源代码并不一定要了解每一个细节,只要关注我们需要了解的源代码即可。
4.1.1 配置Android源代码下载环境
在下载Android源代码之前必须要在Linux终端执行一系列命令来配置下载环境。下面来看看如何安装下载Android源代码的环境。
第1步:创建一个用于存放下载脚本文件(repo)的目录(可将该脚本文件一放到任何目录中,在这里使用~/bin)。
# mkdir ~/bin # PATH=~/bin:$PATH
第2步:下载repo脚本文件(用于下载Android源代码)。
# curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo # chmod a+x ~/bin/repo
repo文件实际上是用 python 脚本对 Git 的封装,主要是为了简化下载 Android 源代码的操作。
第3步:创建用于存放Android源代码的目录(可放在其他目录中)。
# mkdir android_source # cd android_source
第4步:初始化。
# repo init -u https://android.googlesource.com/platform/manifest
上面的命令会要求repo下载最新的Android源代码,也就是master分支。如果想下载其他分支(如android-4.0.1_r1),可以使用下面的命令。
# repo init -u https://android.googlesource.com/platform/manifest -b android-4.0.1_r1
第5步:开始下载Android源代码。
# repo sync
执行上面的命令后,Linux终端一开始会显示一些类似于如图4-1所示的信息。其中,会列出Android源代码中包含当前模块的分支([new branch]和[new tag]后面的都是分支)。在显示完这些信息后,会正式开始下载Android源代码。接下来就是漫长的等待。根据读者网速和机器性能不同,完成下载的时间会有很大差异。但据笔者多次测试,按国内大多数人能达到的网速,至少要4个小时才能下完。所以在下载之前可以准备几部电影,以免下载过程中无事可做。因为Git会尽可能地占用带宽。下载过程中网络几乎做不了其他的事情(通过技术手段限速的除外)。
▲图4-1 下载Android源代码
如果要备份Android源代码,最好不要把版本库(.git目录)删除,否则再执行repo sync命令会重新下载整个Android源代码。如果依靠版本库,执行repo sync命令只会下载变化的部分,如图4-2所示。
▲图4-2 更新最新的Android源代码(增量下载)
4.1.2 Android源代码目录结构解析
经过漫长的等待之后,多达4GB的Android源代码终于下载完了。那么Android源代码到底是什么样的呢?现在就来一窥究竟。
现在进入上一节建立的保存Android源代码的目录(android_source),可以看到如图4-3所示的目录结构(Android 4.0.3的源代码)。
▲图4-3 Android源代码的目录结构
从图4-2的目录结构可以看出,除了Makefile文件外,其他的全是目录。这些目录分别保存了Android源代码的不同子项目。它们的含义如表4-1所示。
表4-1 Android源代码目录含义
续表
4.1.3 下载Android源代码中的一部分
在4.1.1节下载了完整的Android源代码。但如果只需要一部分源代码该怎么办呢?难道要下载所有的Android源代码吗?答案是否定的。只要了解了Android源代码的目录结构,就可以下载任何我们想要的部分。
下载Android源代码中的某个子项目有两种方法。
方法1:使用repo sync命令
假设想单独下载<Android源代码目录>/bootable/recovery项目,可以在repo sync命令后跟工程名(执行repo sync命令前仍然需要使用repo init初始化)。
# repo sync platform/bootable/recovery
执行上面的命令后,会在当前目录下生成两级目录:bootable/recovery,recovery项目的所有代码都在这个目录中(大概3.5MB,读者可以放心下载,一会就下载完了)。虽然成功获取了recovery项目的源代码,但repo sync后面跟的项目名如何获取呢?
读者可以对比platform/bootable/recovery和Android源代码的目录结构就会很容易发现,原来工程名就是要下载的工程所在的路径全名(如bootable/recovery)前面加上platform/。如下载拍照程序如源代码(工程路径:packages/apps/Camera)的命令如下:
# repo sync platform/packages/apps/Camera
现在已初步解决了如何获取工程名的问题,但又产生了新问题。获取工程名的基础是已经下载了完整的Android源代码,再根据源代码的目录结构获取要下载工程的相对路径,最后才和platform/组合成工程名。那如果没获取Android源代码,如何获取工程名呢?答案就在4.1.1节的第4步中。
当执行repo init命令初始化的过程中,会在当前目录生成一个隐藏目录.repo。在.repo目录中有一个非常重要的文件:manifest.xml。该文件包含了当前Android源代码版本库中包含的所有工程的列表。如下面的代码就是manifest.xml文件中的部分内容。每一行表示一个工程。其中path属性表示工程的路径,name属性表示工程名。repo sync命令后跟的就是name属性的值。
<project path="bionic" name="platform/bionic" /> <project path="bootable/bootloader/legacy" name="platform/bootable/bootloader/legacy"/> <project path="bootable/diskinstaller" name="platform/bootable/diskinstaller" /> <project path="bootable/recovery" name="platform/bootable/recovery" />
从 manifest.xml 文件中还可以得知当前下载的是 Android 源代码版本库的那个分支。如果在manifest.xml文件的开始部分找到如下的内容,说明当前下载的Android源代码分支是android-4.0.1_r1.2。
<default revision="refs/tags/android-4.0.1_r1.2" remote="aosp" sync-j="4" />
方法2:使用git clone命令
使用git clone命令下载指定工程源代码的命令格式如下:
git clone https://android.googlesource.com/<project name>
从上面的命令格式可以看出,用 git clone 命令下载 Android 源代码,也需要工程名。如下载recovery工程的命令如下:
git clone https://android.googlesource.com/platform/bootable/recovery
执行上面的命令后,会在当前目录下生成一个recovery目录(包含所有recovery项目的源代码)。从这一点可以看出,用repo sync和git clone命令下载源代码主要有如下区别。
· repo sync会在当前目录生成一个隐藏目录.repo。该目录存放了下载Android源代码所需的全部信息。
· repo sync下载指定工程的源代码会按照实际的路径存放,如bootable/recovery。而git clone会只会建立工程目录(如receovery),并不会建立层次路径结构。
如果直接在浏览器中输入https://android.googlesource.com/platform/bootable/recovery,就会显示如何下载receovery工程的命令(输入其他工程也是一样),如图4-4所示。
▲图4-4 显示下载制定工程的命令的页面
注意
以前Android源代码的下载域名是android.git.kernel.org,但现在Google已经放弃了这个域名。新的域名是android.googlesource.com。
4.1.4 编译Android 源代码
由于 Android源代码中的每一个工程目录都有Android.mk文件,所以在编译整个Android源代码时会递归调用每一个工程目录中的Android.mk文件来编译当前的工程。编译Android 源代码的步骤如下(在编译之前要先进入Android源代码根目录)。
第1步:初始化编译环境
在Linux终端执行如下两条命令中的任何一个。
# source build/envsetup.sh
或
# . build/envsetup.sh
envsetup.sh脚本文件主要用来初始化一些编译命令,如mm、mmm等。执行envsetup.sh命令后会在Linux终端显示如图4-5所示的信息。
▲图4-5 初始化编译环境
在envsetup.sh脚本文件中定义了一些Shell函数。当使用source命令执行envsetup.sh后,就可以在Linux终端执行调用这些函数。其中help就是一个在envsetup.sh脚本文件中定义的命令。通过help命令可以显示在envsetup.sh脚本文件中定义的常用命令,如图4-6所示。
▲图4-6 在envsetup.sh脚本文件中定义的常用命令
其中mm和mmm是两个很重要的命令。这两个命令都可以编译Android源代码中指定的工程,它们的区别是mm命令必须进入指定工程的目录进行编译,而mmm可以在Android源代码目录结构中的任何一级目录编译任意指定的工程,只要指定工程路径即可。例如,要编译Calendar工程,可以使用下面两种方式。
# cd /working/android2.3.4_src # source build/envsetup.sh # cd packages/apps/Calculator # mm
或
# cd /working/android2.3.4_src # build/envsetup.sh # mmm packages/apps/Calculator
如果是第一次使用mm或mmm编译指定的工程,都会输出如图4-7所示的信息。
▲图4-7 编译Calculator工程
第2步:选择目标
使用 lunch 命令设置编译目标。如下面的命令设置编译目标为 full-eng,表示针对所有的移动设备,Android模拟器有效,并打开所有的调试选项(在LogCat视图中会输出调试信息)。
# lunch full-eng
如果只执行lunch命令,会显示如图4-8所示的选择菜单,在android 4.x的源代码中有10个选项。默认是full-eng,可以输入其他的选项。可以输入英文选项名称或选项序号。
▲图4-8 选择一个编译目标
在选择完如图 4-8 所示的某个选项后(需要按回车键),会输出如图 4-9 所示的信息,其中TARGET_BUILD_VARIANT变量保存了选择的编译目标。
▲图4-9 选择编译目标后的输出信息
第3步:编译Android源代码
执行如下命令即可编译Android源代码。
# make
为了缩短编译时间,可以在拥有多CPU、多核、超线程的PC上使用-jn命令行参数。如笔者的PC是4核的,所以如果使用下面的命令编译Android源代码时,make命令就会尽可能使用每一个CPU内核进行编译。
# make –j4
如果在多核的PC上不加-jn命令行参数,make命令只会利用一个CPU核进行编译,这将大大降低Android源代码的编译效率。
在第1步还介绍了mm和mmm命令。这两个命令实际上是Shell函数。其内部也是调用了make命令。如果要编译指定的工程可以直接使用这两个命令。在envsetup.sh脚本文件中还定义了一个m函数用来编译完整的Android源代码。因此如果要编译整个Android源代码,可以使用m代替make。
不仅下载Android源代码需要较长的时间,完全编译Android源代码也需要很长的时间。当执行make -j4命令后就进入了漫长的等待。当数小时后编译完成时,会在Android源代码根目录中生成一个out目录,所有编译的目标文件都在这个目录中。下一节将介绍out目录中包含了哪些东西。
4.1.5 out目录结构分析
out目录是存放编译Android源代码生成的目标文件的默认目录。图4-10左侧黑框中是out目录的结构。右侧黑框中的3个img文件是生成的主要的镜像文件,可以使用Android模拟器或真机中测试。
▲图4-10 out目录的结构
out目录中只有两个直接子目录:host和target。host目录主要是一些编译过程中需要的一些库和工具。如out/host/common/obj/JAVA_LIBRARIES目录中包含编译过程中需要的一些Java Library。out/host/linux-x86/bin目录是中Linux平台下运行的一些工具,如aapt、adb、aidl等。由于笔者在Ubuntu Linux下编译Android源代码,因此生成的这些工具都在linux-x86目录中。当在其他平台上编译Android源代码是会生成可在相应平台运行的工具。
注意
host目录中的库和工具都是中PC上运行的。也就是说,在编译Android源代码时会生成在两种平台运行的库和工具。一种就是直接运行在Android平台上的(基于ARM架构,存放在out/target目录中),另外一种就是运行在编译Android源代码的平台上的(如Ubuntu Linux),存放在out/host目录中。
out/target目录中的目标文件(.jar、.so、.img等)都可以在Android系统上运行。out/target目录包含两个直接子目录:common和product。其中,common主要包含了在Android系统上运行的程序需要的 Java 库(out/target/common/obj/APPS、out/target/common/obj/JAVA_LIBRARIES)、存放资源 ID 的R.java(out/target/common/R/android/R.java)、官方文档(out/target/common/docs/doc-comment-check)等内容。
out/target/generic目录包含了最终要使用的目标文件。其中包括3个重要的img文件:system.img、ramdisk.img和userdata.img。其中,out/target/generic/system目录就是system.img解压后的目录。Android系统中大多数程序都在system目录中,包括apk应用程序(out/target/generic/system/app)。ramdisk.img文件是out/target/generic/root目录的压缩包,userdata.img文件是out/target/generic/data的压缩包。
out/target/generic/system目录的结构和Linux根目录类似(其实Android就是Linux系统的变种),如bin、usr、lib等。当然,Android也增加了很多特有的目录。如apps目录用于存储apk应用程序, media目录用于存储与Android系统的多媒体相关的资源(如音频、启动动画等)。
4.1.6 将自己的APK作为Android内置程序发布
为了测试system.img文件,本节将一个自己生成的apk程序嵌入system.img文件中,这样在测试system.img文件时这个apk程序就和其他原生的应用程序一样出现在应用程序列表中,而且该程序无法卸载(除非获取root权限,并直接将apk文件删除)。
将apk程序嵌入system.img文件最简单的方式就是直接将apk文件复制到system/app目录下,然后再从system目录生成system.img文件。不过在system/app文件中不光有apk文件,还有与apk文件对应的odex文件。所有系统自带的程序都有与之对应的odex文件。如Calendar.apk文件有与之对应的Calendar.odex文件。
研究过apk文件结构的读者应该会很清楚,apk文件实际上是zip格式的,直接解压即可。在apk文件中有一个classes.dex文件。该文件是由Java和NDK源代码编译而成的Dalivk虚拟机格式的目标文件。在system/app目录中的odex文件就是将apk文件和classes.dex文件分离后产生的,但并不是将classes.dex文件直接复制出来改个名就行,而要重新使用mm或mmm命令编译源代码生成apk和odex文件。
在<光盘根目录>/sources/showdatetime 目录中提供了一个用于显示当前日期和时间的 Android应用程序。读者也可以使用自己的Android 工程,但要为该工程编写一个Android.mk 文件,并将Android.mk文件放在Android工程的根目录。showdatetime工程的Android.mk文件的内容如下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SDK_VERSION := current LOCAL_PACKAGE_NAME := showdatetime include $(BUILD_PACKAGE) include $(CLEAR_VARS)
将showdatetime目录复制到<Android源代码目录>/packages/apps目录中。然后在Linux终端进入<Android源代码目录>/packages/apps/showdatetime目录,执行mm命令编译showdatetime工程。如果编译成功,会在<Android源代码目录>/out/target/product/generic/system/app目录中生成showdatetime.apk和showdatetime.odex文件。这里的showdatetime.apk文件中并没有classes.dex文件。
注意
编写可以嵌入ROM里的Android应用程序时不能在布局、菜单等资源文件中为属性直接赋字符串值,应将字符串定义成资源,然后再引用。如android:text属性不能直接设置成“确定”,应使用<string name="ok">确定</string>定义成字符串资源,再使用@string/ok 设置 android:text 属性。如果直接用字符串常量设置属性,在使用mm编译工程时会抛出This attribute must be localized编译错误。表明在编译时强制要求字符串资源本地化。
4.1.7 用模拟器测试system.img文件
在4.1.4节已经完全编译了Android源代码,并在<Android源代码目录>/out/target/product/generic目录生成了 system.img 文件。但在上一节添加了一个 showdatetime 程序,因此需要重新生成system.img文件。
生成system.img文件需要一个mkyaffs2image命令行工具。该命令行工具可以将system目录打包成system.img文件。读者可以在<Android源代码目录>/out/host/linux-x86/bin目录找到该文件,也可以使用<光盘根目录>/resources/tools目录中的mkyaffs2image。
在Linux终端进入<Android源代码目录>/out/target/product/generic目录,执行如下的命令重新生成system.img文件(最好先将原来的system.img文件备份)。
# mkyaffs2image system system.img
执行下面的命令可以使用模拟器测试刚生成的system.img文件(需要在PATH环境变量中添加<Android SDK根目录>/tools目录)。
# emulator -avd myavd -system system.img -data userdata.img
其中 myavd 是一个已创建好的 AVD。执行上面的命令后,Android 模拟器就会使用新的系统镜像(system.img)和新的用户数据镜像(userdata.img)启动,我们会在程序列表中看到“显示当前日期和时间”程序。该程序是嵌入 Android 系统中的,因此无法通过常规方法卸载。除非将showdatetime.apk文件和showdatetime.odex文件删除,再重新生成system.img文件,并使用该文件启动Android模拟器才能删除该程序。
4.2 下载和编译Linux内核源代码
4.1节下载的Android源代码并没有包含Linux内核源代码,因此,要想使用Linux内核,必须要单独下载Linux内核源代码。Android并不能直接使用从www.kernel.org下载的Linux内核。而必须从Google提供的网址下载可以中Android中使用的Linux内核源代码。
4.2.1 下载Linux内核源代码
执行下面的命令可以下载最新的Linux内核源代码。
# git clone https://android.googlesource.com/kernel/common.git
执行上面的命令可能需要较长的时间下载 Linux 内核。下载完后,在当前目录下会有一个common目录,进入该目录,执行下面的命令查看当前有哪些远程版本库。
# git branch –a
读者可以根据版本库情况从版本库导出最新的 Linux 内核,例如,可以使用下面的命令导出Linux 3.0的内核。
# git checkout -b android-3.0 remotes/origin/android-3.0
Google专门为Android模拟器提供了一个Linux内核(goldfish),可以使用相面的命令下载该内核。
# git clone http://android.googlesource.com/kernel/goldfish.git
下载完后,进入goldfish目录,执行下面的命令导出goldfish。
# git checkout -b android-goldfish-2.6.29 remotes/origin/android-goldfish-2.6.29
4.2.2 Linux内核源代码的目录结构
读者可以打开下载完的Linux内核目录,并对照表4-2来了解Linux内核中各个目录的含义。
表4-2 Linux内核源代码目录的含义
续表
4.2.3 安装Android内核的编译环境
在Ubuntu Linux下编译Linux内核需要安装交叉编译器。所谓交叉编译器就是指在A架构的CPU上编译可在B架构CPU上运行的程序。如在X86 架构上编译可在ARM架构(多数移动设备使用的CPU架构)上运行的程序。那么为什么要这么做呢?主要是因为编译器无法在目标平台上安装或目标平台性能较低,所以不得已才在性能较高限制较少的平台上进行编译。各位读者可以想象,如果在手机上编译庞大的Linux内核会是怎样的情景呢?
在第2章已经介绍过如何安装交叉编译器。在编译Linux内核之前,需要配置Linux内核源代码根目录下的Makefile文件。现在打开Makefile文件,找到ARCH和CROSS_COMPILE,并将这两个变量设置成如下的值。
ARCH ?= arm CROSS_COMPILE?= arm-none-linux-gnueabi-
其中 ARCH 变量表示将 Linux 源代码编译成可在 ARM 架构上运行的 Linux 内核。CROSS_COMPILE变量表示交叉编译器的前缀。读者可以打开交叉编译者的bin 目录,可以看到很多以arm-none-linux-gnueabi-开头的命令,如arm-none-linux-gnueabi-gcc、arm-none-linux-gnueabi-gdb等。由于编译Linux内核可能会使用到很多这样的命令,为了方便,Linux内核允许值只设置这些命令的前缀,就可以找到相应的编译命令。
经过笔者测试,goldfish 和最新的 Linux 内核使用第 2 章下载的交叉编译器都没问题。只有OK6410开发板带的Linux内核(Linux 2.6.36)使用第2章下载的交叉编译器生成的zImage文件无法正常启动开发板。为此在随书光盘中提供了一个较低版本的交叉编译器。压缩包路径如下:
<光盘根目录>/resources/compilers/arm-none-linux-gnueabi-arm-2008q3-72-for-linux.tar.bz2
读者可按如下步骤安装交叉编译器。
第1步:准备工作
将arm-linux-gcc-4.3.2.tgz文件和arm-none-linux-gnueabi-arm-2008q3-72-for-linux.tar.bz2文件复制到工作目录。
第2步:解压编译器
使用下面的命令解压上面的两个压缩文件。
# tar zxvf arm-linux-gcc-4.3.2.tgz –C / # tar jxvf arm-none-linux-gnueabi-arm-2008q3-72-for-linux.tar.bz2 –C /
第3步:验证交叉编译器是否安装成功
# ls /usr/local/arm -l
执行上面的命令后如果输出如图4-11所示的信息,表明交叉编译器安装成功。
▲图4-11 验证交叉编译器是否安装成功
第4步:安装libncurses5
# apt-get install libncurses5-dev
安装libncurses5主要是为了配置内核(make menuconfig)。libncurses5可以为字符界面的Linux终端提供一个美观的界面。
在随书光盘中带的虚拟环境包含了已安装完的交叉编译器,路径如下:
/root/compilers/arm-none-linux-gnueabi
在虚拟环境中PATH变量是指向第2章下载的交叉编译器的,为了方便使用上面路径的交叉编译器,在/root/kernel/linux_kernel_2.6.36 目录中提供了一个 compile.sh 脚本文件,读者可以在虚拟环境中直接执行compile.sh脚本文件来编译Linux内核。
4.2.4 配置和编译Linux内核
假设Linux内核源代码目录是/root/linux_kernel,使用下面的命令可以编译Linux内核。
# export PATH=/root/compilers/arm-none-linux-gnueabi/bin:$PATH # cd ~/linux_kernel # make clean # make
其中make clean命令用于清除大多数由编译生成的文件(.o、.ko等文件),但保留配置文件。除了make clean命令外,还有另外两个清除得更彻底的命令:make mrproper和make distclean。这两个命令的描述如下。
□ make mrproper:清除所有由编译产生的文件 + 清除配置文件。
该命令除了会清除所有由编译参赛的文件外,还会清除 scripts/basic、scripts/kconfig、include/config include/generated arch/x86/include/generated等目录的配置文件以及Linux内核源代码根目录的.config、.config.old文件。
□ make distclean:make mrproper +删除编辑器留下的备份文件和补丁文件。
该命令除了会清除make mrproper命令清除的所有文件外。还会从scripts/mod、scripts/selinux/genheaders、scripts/selinux/mdp等目录清除相应的备份文件和补丁文件以及.version 和include/linux/version.h文件。因此make distclean是最彻底的清除命令。
一般新下载的Linux源代码根目录都没有.config文件。而这个文件决定了编译生成的Linux内核由哪些功能和模块组成。因此,第一次拿到Linux内核时应先使用下面的4个命令中的一个配置Linux内核,并中Linux内核源代码根目录产生一个.config文件。当然,在配置Linux内核前一般需要确定自己的需求,例如,需要什么样的驱动模块、是否支持调试、支持的CPU类型、支持的文件系统等。
下面4个配置命令必须在Linux源代码根目录执行。
□ make config:该命令以文本形式配置Linux内核。执行该命令后会显示如图4-12所示的若干个询问项,要求用户做出回答。配置项后面中括号中的值是该配置项当前的值。如果中括号中有多个值,第一个值是默认值。如果不设置,直接按回车就会保留默认值。
▲图4-12 使用make config命令配置Linux内核
□ make menuconfig:以如图4-13所示的字符界面风格的菜单形式配置Linux内核。
▲图4-13 使用make menuconfig命令配置Linux内核
在如图4-13所示的配置界面后面带“--->“的菜单项说明还有子菜单。按回车键就可以进入子菜单。很多菜单项前面有中括号([ ])。中括号里面可以是“M”或“*”,也可以为空。有的菜单项前的中括号里只能设置“*”或空。中括号里的值表明当前菜单项对应的Linux内核组件嵌入Linux内核的方式。[M]表示以模块形式(.ko文件)动态安装在Linux内核中。[Y]表示Linux内核组件会嵌入Linux内核。当Linux内核成功启动后,这些组件会自动装载。如果为空表明该Linux内核组件不会包含到当前的Linux内核中。通过按“M”键可以将中括号中的值设置成“M”,按“Y”键可以将中括号中的值设置成“Y”。按空格键可以在[M]、[Y]和空格之间进行切换。最后退出设置界面后(通过<Exit>退出),如果修改了设置项,会显示如图4-14所示的界面,询问时候保持刚才的设置。如果选择“Yes”,则会根据刚才的设置重新生成.config文件。
▲图4-14 询问是否保持设置
□ make oldconfig:make oldconfig与make config类似,也是以字符界面逐项要求用户配置。但make oldconfig显示的要求用户参与的配置项要比make config少。大多数配置项都会给出默认值,并不需要用户进行配置。只有后面带(NEW)的配置项才需要用户配置(这些配置项都是当前Linux内核版本新加入的功能),如图4-15所示。
▲图4-15 使用make oldconfig命令配置Linux内核
□ make xconfig:以图形界面形式配置Linux内核。由于make xconfig命令显示的图形界面使用了QT的共享库,因此,如果执行make xconfig命令的Ubuntu Linux未安装QT,可以使用下面的命令安装QT。
# apt-get install libqt4-dev
安装完QT后,执行make xconfig命令后,会显示如图4-16所示的界面。在界面中可以通过上面的“保存”安装保存设置,也可以使用“打开”按钮打开其他的配置文件。
▲图4-16 使用make xconfig命令配置Linux内核
在配置中有一项很重要,就是交叉编译器的签名。在上一节已经在配置交叉编译器时设置了Makefile文件的CROSS_COMPILE变量,该变量的值就是交叉编译器的前缀。我们也可以通过上面介绍的4个命令来设置交叉编译器的前缀。笔者推荐使用make menuconfig命令,因为执行该命令并不需要安装额外的Library,而且可以用可视化方式配置Linux内核。
执行make menuconfig命令,进入“General setup”菜单项的子菜单。选择“Cross-compiler tool prefix)菜单项,如图4-17所示。
▲图4-17 General setup子菜单
按回车键后,会弹出一个输入框,要求输入交叉编译器的前缀,如图 4-18 所示。输出正确的前缀后,退出保存设置即可。
▲图4-18 设置交叉编译器的前缀
在Linux内核中有很多预定义的配置文件,但文件名不叫.config,而且也不在Linux源代码根目录中。这些配置文件分布在arch/<cpu>/configs目录中。其中<cpu>表示不同的CPU架构。例如,与arm架构有关的配置文件就在arch/arm/configs目录中。在configs目录中包含了若干个配置文件,每个配置文件都细分了ARM CPU的类型,例如,本书使用的是S3C6410 ARM 11,因此,可以将s3c6400_defconfig配置文件复制到Linux内核源代码根目录,并改名为.config,然后,再使用make menuconfig命令配置Linux内核。这样可以省去配置所有配置项的麻烦。
在结束所有的设置后,执行make命令编译Linux内核。如果成功编译了Linux内核,会显示如图4-19所示的信息。
▲图4-19 成功编译Linux内核
成功编译 Linux 内核后,会在<Linux 内核源代码根目录>/arch/arm/boot 目录生成一个 zImage文件。该文件就是Linux内核的二进制版本。可以直接使用zImage安装到开发板上(见下一章的介绍),也可以使用下面的命令在Android模拟器中测试Linux内核(需要使用由goldfish编译而成的zImage文件)。
# emulator –avd myavd -kernel /root/kernel/goldfish/arch/arm/boot/zImage
其中myavd是一个AVD名称。
4.3 小结
Android移植主要就是Linux内核的移植。而Linux内核移植主要是Linux驱动的移植。所以为了开发和测试Linux驱动。需要在Ubuntu Linux下搭建两套开发环境:Android应用程序开发环境和Linux内核开发环境。本章的主要目的就是介绍如何搭建这两种开发环境。其中Linux内核开发环境是本章的重点。为了方便读者,在随书光盘中已带了本书需要的全部工具和源代码。如果读者不想重新配置开发环境,在随书光盘中还为读者提供了一个VMWare虚拟机环境。系统为Ubuntu Linux 11.10,登录用户名是root,登录密码是androidkernel。所有的环境都配置完了,读者可以直接在上面做实验。