2016/10/21
中科大负责镜像的同学回复说问题已解决。2016/10/11
关于AOSP的镜像,中科大的目前不能正常使用。
可以暂时使用清华的源。
这是之前的笔记,主要记录了如何搭建Android源代码编译环境进行编译。里边有一些优化和问题的记录,可能会帮助大家更快地编译出来自己的Android系统。
为了方便,我使用了VMWare虚拟机搭载Ubuntu 14.04作为我的操作系统。因为虚拟机确实比较方便,在这个硬盘便宜的时代,多占一些空间还是问题不大的。而且可以方便我们来回移动虚拟机,并且虚拟机文件在Windows系统和Mac OSX系统上都可以比较良好的运行。(Windows上用VMWare Workstation,Mac OSX上用VMWare Fusion,一家公司的产品,虚拟机文件兼容性没得说。)
虚拟机的硬盘空间我分配了上限120G(现在发现勉强够用,如果开ccache就有点够呛了),大家可以适当调大些。
内存根据你的实际情况,越大越好,因为在编译的后期很有可能会出现内存不足导致的编译失败。我分配了10G物理内存出来。
CPU核心也是看你的物理机的CPU,后边的”make -jN”命令里边的N和你的CPU核心数(总线程数)直接相关。
在某篇文章中看到这么一句话,”大家编译的时候总会遇到这样那样的问题,而且每次编译遇到的问题还不一样”。对这一点深有体会。所以我们可以在不同的阶段复制一份虚拟机文件作为备份,以后你想回到哪一个阶段都比较方便(或者你也可以把任何一个阶段的虚拟机分享给别人)。
为此,我专门买了一个大的移动硬盘存储了多份虚拟机文件。我备份了下面的几个阶段:
我并没有使用VM的快照功能,因为在操作虚拟机的时候都是直接将虚拟机文件复制到电脑的主硬盘上进行的,因为是SSD所以速度快一些,但是容量有限,所以能省则省。对于动辄几十G的操作变化来说,还是不要用快照了。
将Software&Updates的服务器切换到中国,我这里选择的是163
网上找了一圈,发现了几个国内的镜像
这3个选择哪个呢?
东软的帮助里边只有SDK代理的配置,而没有AOSP的配置。
清华大学的同步源使用的是https协议,限制速度了,同步比较慢。
对比起来,中科大的源,速度较快,能达到5M+/S左右。下载源代码大概花了1个多小时,比Google源快太多了。
现在已经限制速度了,最快600K+/S左右
不同的分支需要不同的Java版本来进行编译
分支 | Java版本 |
---|---|
Master分支 | OpenJDK 8 |
5.x - 6.0分支 | OpenJDK 7 |
2.3.x - 4.4.x | Java JDK 6 |
这里我下载的是5.1.1版本的源代码,需要安装OpenJDK 7,安装指令如下
$ sudo apt-get install openjdk-7-jdk
如果你要下载Master分支的源代码,则需要安装OpenJDK 8,安装指令如下(推荐16.04)
$ sudo apt-get update
$ sudo apt-get install openjdk-8-jdk
14.04安装OpenJDK 8比较麻烦,具体参考For Ubuntu LTS 14.04
Ubuntu 14.04需要的软件包如下
$ sudo apt-get install git-core gnupg flex bison gperf build-essential \
zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 \
lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache \
libgl1-mesa-dev libxml2-utils xsltproc unzip
指令如下
wget -S -O - http://source.android.com/source/51-android.rules | sed "s//$USER/" | sudo tee >/dev/null /etc/udev/rules.d/51-android.rules; sudo udevadm control --reload-rules
将上面的$USER更改为你的用户名
上面这个文件所预设的设备有限,如果你的设备无法正常访问(如提示没有权限),可以使用lsusb
命令查询设备的id,并将其配置到上述文件中(/etc/udev/rules.d/51-android.rules
)
比如,我的一个”未知”设备的信息如下:
$ lsusb
Bus 001 Device 002: ID 18d1:4ee7 Google Inc.
...
我们根据Google的写法,在文件最后加上
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4ee7", MODE="0600", OWNER="$USER"
repo是Google开发的一个Android源代码同步工具,如果你直接在Google官方下载代码,那么按照官网的教程安装就可以了。
如果你要使用国内镜像的源进行下载,那么要对repo进行一下简单的修改。它本身是一个python脚本文件,所以实际上是修改里边的REPO_URL
变量。只不过中科大已经改好了,你直接从它那里下载repo不用修改就行了;而清华大学的需要你从Google下载好repo然后手动修改REPO_URL
变量。
这里以中科大为例,贴一下指令
mkdir ~/bin
PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
## 如果上述 URL 不可访问,可以用下面的:
## curl https://storage-googleapis.lug.ustc.edu.cn/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
repo是一个python文件。
REPO_URL
变量原始值为https://gerrit.googlesource.com/git-repo
中科大将其修改为了https://gerrit-googlesource.proxy.ustclug.org/git-repo
工作目录的名字可以随意起
mkdir WORKING_DIRECTORY
cd WORKING_DIRECTORY
由于网络不稳定因素,所以可以采用中科大或者清华大学镜像源推荐的”先下载镜像tar包,然后再repo同步至最新代码”的方法。(具体步骤参见上面的链接)
下载好之后,将其解压到指定的目录。下面是一个示例:
$ tar xvf ~/Downloads/aosp-20171001.tar -C ./Volumes/android/aosp/
注意,上面的镜像包只是打包,并没有压缩。
然后
如果您已经从官方同步了 AOSP 仓库,现在希望使用科大的 AOSP 仓库,请修改
.repo/manifests.git/config
,将url = https://android.googlesource.com/platform/manifest
修改成
url = git://mirrors.ustc.edu.cn/aosp/platform/manifest
即可。
然后执行repo sync
进行同步
请注意保持上述文件中的地址与
repo
文件中的地址为同一个源的地址,以免发生错误。
5.1.1版本已经不在Master分支了,所以我们需要手动指定分支。具体的分支名称可以从 Source Code Tags and Builds 查询到,5.1.1如下
LMY48Y | android-5.1.1_r26 | Lollipop | Nexus 6 |
---|
所以我的初始化指令就是
repo init -u git://mirrors.ustc.edu.cn/aosp/platform/manifest -b android-5.1.1_r26
如果你要同步Master分支,那么指令如下
repo init -u git://mirrors.ustc.edu.cn/aosp/platform/manifest -b master
repo sync
强烈建议大家使用国内镜像源,一个多小时就能下载完成。
注意这一步是可选的,它的主要作用是你重新编译(make clean之后)的时候回大大加快速度。官方建议缓存的大小为50 - 100G,注意保证你的磁盘可用空间。因为我使用的是模拟器,这个会使得虚拟机文件大很多,所以我并没有开启这个缓存。
但是,命令还是贴在这里。
设置环境变量(可以写在.bashrc或类似的文件中)
export USE_CCACHE=1
export CCACHE_DIR=//.ccache // optional
执行命令
prebuilts/misc/linux-x86/ccache/ccache -M 50G
注意,ccache可以手动指定目录,如不指定,默认目录是在~/.ccache
。
官方文档介绍的是配置专有的二进制包(其实就是硬件驱动),因为这些是有专利的,所以不能开源,只能提供二进制包。
官方只提供Google Nexus系列的驱动,而我暂时只运行emulator,所以跳过这一节。
如果你的target是Nexus系列的硬件,请参考 Obtain proprietary binaries 下载对应的驱动。
如果是Nexus 5x和6p,在上面的地址是找不到对应的proprietary binaries的,可以从 Binaries Preview for Nexus Devices 找到。
关于上面这个问题,有几个讨论作为书签记录下来:
两个命令,执行哪个都可以
$ source build/envsetup.sh
或者
$ . build/envsetup.sh
编译6.0或Master分支
Android 6.0之后,AOSP编译引入了Jack工具链,它替换了之前几个工具的组合(javac, ProGuard, jarjar和dx)。引入它的目的是为了提高编译速度。
幸运的是,我们无需做任何额外的事情来启用Jack,任然使用之前的编译命令即可。
更多信息请参考我翻译的文章 使用Jack编译,原文是 Compiling with Jack。
执行lunch
命令,会出现一个菜单让你选择build target。
这里我选择的是aosp_x86_64-eng,即x86_64平台的模拟器,如果是arm,那么运行速度实在是太慢了。
编译时我们可以选择同时并行运行的任务,推荐配置是你CPU线程数量的1-2倍,我给虚拟机设置了4个线程的核心,那么命令就是
$ make –j8
如果你需要在不同的分支之间切换,那么请参考 AOSP分支切换
编译了3个多小时(正在编译libwebviewchromium.so),出现了下面的错误提示
collect2: error: ld terminated with signal 9 [Killed]
经过搜索,遇到这个错误的大有人在,原来是内存不够用了,这时候可以增加物理内存或者swap分区。因为Virtual Memory = RAM + Swap space/file。
检查一下配置发现,虚拟机的物理内存分配了4G,swap分区为2.1G,一共是6.1G。
可以选择增加swap分区,不过增加物理内存是更加简便易行(并且一劳永逸)的方法。
增加到了8G试试。
重新执行make –j8依然失败,无奈,make clean,然后将ccache设置为30G(磁盘快用完了),重新执行make –j8
由于是在虚拟机里边进行编译,而物理硬盘又太小,所以暂且不开ccache了。
重新编译了一次,这次物理直接物理内存10G,swap还是2.1G,执行的是make –j8,3个半小时之后(其实我又换了一份没有编译过的虚拟机重新编译),提示编译失败,相应的错误信息有(比较多,这两句看着像错误原因):
Dumping all threads without appropriate locks held: thread list lock mutator lock
make: *** [out/target/product/generic_x86_64/obj/APPS/Contacts_intermediates/x86_64/package.odex] Aborted (core dumped)
猜测可能是由于多线程的问题造成编译失败,所以将线程数改为4个,执行make –j4,7分钟之后编译成功。
详细的错误日志如下
Out of memory error (version 1.2-rc4 'Carnac' (298900 f95d7bdecfceb327f9d201a1348397ed8a843843 by [email protected])).
GC overhead limit exceeded.
Try increasing heap size with java option '-Xmx '.
Warning: This may have produced partial or corrupted output.
详细的错误日志里边列出了问题并且已经给出了解决方案 - 增加Java虚拟机的-Xmx
大小,即设置一个较大的堆内存上限。
可以修改Jack的配置文件prebuilts/sdk/tools/jack-admin
。
这时一个管理Jack的shell脚本,找到start-server
函数,直接修改其启动参数,由原来的
JACK_SERVER_COMMAND="java -XX:MaxJavaStackTraceDepth=-1 -Djava.io.tmpdir=$TMPDIR $JACK_SERVER_VM_ARGUMENTS -cp $LAUNCHER_JAR $LAUNCHER_NAME"
更改为
JACK_SERVER_COMMAND="java -XX:MaxJavaStackTraceDepth=-1 -Djava.io.tmpdir=$TMPDIR $JACK_SERVER_VM_ARGUMENTS -Xmx4096m -cp $LAUNCHER_JAR $LAUNCHER_NAME"
此时Jack服务器仍然在后台执行,所以我们需要将其停止,然后重启启动(make
会自动启动Jack服务器)才能使得修改后的参数生效。
我们执行下面的命令
$ ./prebuilts/sdk/tools/jack-admin stop-server
然后我们重新执行make -jN
命令(N
是你前边设置的并行任务数量)开始编译。
我们可以启动
jconsole
来对比查看Jack服务器更改设置前后的Maximum heap size
,来确认设置是否已经生效。这里有一个问题,通过
jconsole
看到的上述值为”3728384kb”,换算一下是”3641m”,并不等于”4096m”,不知原因为何。
在Ubuntu 16.04上编译Android 6.0时有可能会碰到这个错误,错误信息如下
...
prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8//x86_64-linux/bin/ld: error: out/host/linux-x86/obj32/SHARED_LIBRARIES/libart_intermediates/arch/x86/quick_entrypoints_x86.o: unsupported reloc 43 against global symbol art::Runtime::instance_
...
prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8//x86_64-linux/bin/ld: error: out/host/linux-x86/obj/SHARED_LIBRARIES/libart_intermediates/arch/x86_64/quick_entrypoints_x86_64.o: unsupported reloc 42 against global symbol art::Runtime::instance_
...
out/host/linux-x86/obj/SHARED_LIBRARIES/libart_intermediates/arch/x86_64/quick_entrypoints_x86_64.o:function art_quick_deoptimize: error: unsupported reloc 42
clang: error: linker command failed with exit code 1 (use -v to see invocation)
build/core/host_shared_library_internal.mk:51: recipe for target 'out/host/linux-x86/obj/lib/libart.so' failed
make: *** [out/host/linux-x86/obj/lib/libart.so] Error 1
warning: string 'gsm_alphabet_default_charset' has no default translation.
问题的原因在于aosp中的预编译好的ld
程序存在bug,我们使用Ubuntu系统默认的ld
来替换它。具体步骤如下:
将aosp中的ld
改名
$ cd prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/x86_64-linux/bin
$ mv ld ld.old
使用soft link,链接到全局ld
$ ln -s /usr/bin/ld.gold ld
上面的操作可以解决问题,但是引来了2个问题:
ld.gold
,而不是ld/ld.bfd
?初步观察结果:
aosp中的ld
就是ld.gold
,虽然不是链接过去,但是你可以通过md5sum
发现二者是同一个文件。
所以这里也链接到全局的ld.gold
。
ld
是什么,ld.gold
与ld.bfd
到底区别在哪里呢?