获取OpenJDK源码有两种方式,其中一种是通过Mercurial代码版本管理工具从Repository中直接取得源码(Repository地址:http://hg.openjdk.java.net/jdk7u/jdk7u),获取过程如以下代码所示。
hg clone http://hg.openjdk.java.net/jdk7u/jdk7u-dev
cd jdk7u-dev
chmod 755 get_source.sh
./get_source.sh
这是最直接的方式,从版本管理中看变更轨迹比看Release Note效果更好。但不足之处是速度太慢,虽然代码总容量只有300 MB左右,但是文件数量太多,全部复制到本地需要数小时。另外,考虑到Mercurial不如Git、SVN、ClearCase或CVS之类的版本控制工具那样普及,建议采用第二种方式,即直接下载官方打包好的源码包,读者可以从Source Bundle Releases页面(地址:http://jdk7.java.net/source.html)取得打包好的源码,到本地直接解压即可。一般来说,源码包大概一至两个月左右会更新一次,虽然不够及时,但比起从Mercurial复制代码的确方便和快捷许多。
构建编译环境
对于MacOS来说
只有在OpenJDK 7u4和之后的版本才能编译出Mac OS系统下的JDK包,之前的版本虽然在源码和编译脚本中也包含了Mac OS目录,但是尚未完善。
对于Mac OS,需要安装最新版本的XCode和Command Line Tools for XCode,在Apple Developer网站(https://developer.apple.com/)上可以免费下载,这两个SDK包提供了OpenJDK所需的编译器以及Makefile中用到的外部命令。另外,还要准备一个6u14以上版本的JDK,因为OpenJDK的各个组成部分(Hotspot、JDK API、JAXWS、JAXP……)有的是使用C++编写的,更多的代码则是使用Java自身实现的,因此编译这些Java代码需要用到一个可用的JDK,官方称这个JDK为“Bootstrap JDK”。如果编译OpenJDK 7,Bootstrap JDK必须使用JDK6 Update 14或之后的版本。最后需要下载一个1.7.1以上版本的Apache Ant,用于执行Java编译代码中的Ant脚本。
对于Linux来说
所需要准备的依赖与Mac OS差不多,Bootstrap JDK和Ant都是一样的,在Mac OS中GCC编译器来源于XCode SDK,而Ubuntu中GCC应该是默认安装好的,需要确保版本为4.3以上,如果没有找到GCC,安装binutils即可,在Ubuntu 10.10下编译OpenJDK 7u4所需的依赖可以使用以下命令一次安装完成。
sudo apt-get install build-essential gawk m4 openjdk-6-jdk
libasound2-dev libcups2-dev libxrender-dev xorg-dev xutils-dev
x11proto-print-dev binutils libmotif3 libmotif-dev ant
进行编译
需要下载的编译环境和依赖项目都准备齐全了,最后还需要对系统的环境变量做一些简单设置以便编译能够顺利通过。OpenJDK在编译时读取的环境变量有很多,但大多都有默认值,必须设置的只有两个:LANG和ALT_BOOTDIR,前者是设定语言选项,必须设置为:
export LANG=C
否则,在编译结束前的验证阶段会出现一个HashTable内的空指针异常。另外一个ALT_BOOTDIR参数是前面提到的Bootstrap JDK,在Mac OS上设为以下路径,其他操作系统对应调整即可。
export ALT_BOOTDIR=/Library/Java/JavaVirtualMachines/jdk1.7.0_04.jdk/Contents/Home
另外,如果之前设置了JAVA_HOME和CLASSPATH两个环境变量,在编译之前必须取消,否则在Makefile脚本中检查到有这两个变量存在,会有警告提示。
unset JAVA_HOME
unset CLASSPATH
编译shell脚本:
#环境变量设置
#语言选项,这个必须设置,否则编译好后会出现一个HashTable的NPE错
export LANG=C
#Bootstrap JDK的安装路径。必须设置
export ALT_BOOTDIR=/Library/Java/JavaVirtualMachines/jdk1.7.0_04.jdk/Contents/Home
#允许自动下载依赖
export ALLOW_DOWNLOADS=true
#并行编译的线程数,设置为和CPU内核数量一致即可
export HOTSPOT_BUILD_JOBS=6
export ALT_PARALLEL_COMPILE_JOBS=6
#比较本次build出来的映像与先前版本的差异。这对我们来说没有意义,
#必须设置为false,否则sanity检查会报缺少先前版本JDK的映像的错误提示。
#如果已经设置dev或者DEV_ONLY=true,这个不显式设置也行
export SKIP_COMPARE_IMAGES=true
#使用预编译头文件,不加这个编译会更慢一些
export USE_PRECOMPILED_HEADER=true
#要编译的内容
export BUILD_LANGTOOLS=true
#export BUILD_JAXP=false
#export BUILD_JAXWS=false
#export BUILD_CORBA=false
export BUILD_HOTSPOT=true
export BUILD_JDK=true
#要编译的版本
#export SKIP_DEBUG_BUILD=false
#export SKIP_FASTDEBUG_BUILD=true
#export DEBUG_NAME=debug
#把它设置为false可以避开javaws和浏览器Java插件之类的部分的build
BUILD_DEPLOY=false
#把它设置为false就不会build出安装包。因为安装包里有些奇怪的依赖,
#但即便不build出它也已经能得到完整的JDK映像,所以还是别build它好了
BUILD_INSTALL=false
#编译结果所存放的路径
export ALT_OUTPUTDIR=/Users/IcyFenix/Develop/JVM/jdkBuild/openjdk_7u4/build
#这两个环境变量必须去掉,不然会有很诡异的事情发生(我没有具体查过这些"诡异的
#事情",Makefile脚本检查到有这2个变量就会提示警告)
unset JAVA_HOME
unset CLASSPATH
make 2>&1|tee $ALT_OUTPUTDIR/build.log
全部设置结束之后,可以输入make sanity来检查我们前面所做的设置是否全部正确。Makefile的Sanity检查过程输出了编译所需的所有环境变量,如果看到“Sanity check passed.”,说明检查过程通过了,可以输入“make”执行整个OpenJDK编译(make不加参数,默认编译make all),使用Core i7 3720QM/16GB RAM的MacBook机器,启动6条编译线程,全量编译整个OpenJDK大概需20分钟,编译结束后,将输出类似下面的日志清单所示内容。如果之前已经全量编译过,只修改了少量文件,增量编译可以在数十秒内完成。
#--Build times----------
Target all_product_build
Start 2012-12-13 17:12:19
End 2012-12-13 17:31:07
00:01:19 corba
00:01:15 hotspot
00:00:14 jaxp
00:7:21 jaxws
00:8:11 jdk
00:00:28 langtools
00:18:48 TOTAL
-------------------------
编译完成之后,进入OpenJDK源码下的build/j2sdk-image目录(或者build-debug、build-fastdebug这两个目录),这是整个JDK的完整编译结果,复制到JAVA_HOME目录,就可以作为一个完整的JDK使用,编译出来的虚拟机,在-version命令中带有用户的机器名。
>./java-version
openjdk version"1.7.0-internal-fastdebug"
O p e n J D K R u n t i m e E n v i r o n m e n t(b u i l d 1.7.0-i n t e r n a l-f a s t d e b u g-icyfenix_2012_12_24_15_57-b00)
OpenJDK 64-BitServer VM(build 23.0-b21-fastdebug,mixed mode)
如果我们并不关心JDK中HotSpot虚拟机以外的内容,只想单独编译HotSpot虚拟机的话(例如调试虚拟机时,每次改动程序都执行整个OpenJDK的Makefile,速度肯定受不了,那么使用hotspot/make目录下的Makefile进行替换即可,其他参数设置与前面是一致的,这时候虚拟机的输出结果存放在build/hotspot/outputdir/bsd_amd64_compiler2目录
在不同机器上,最后一个目录名称会有所差别,bsd表示Mac OS系统(内核为FreeBSD),amd64表示是64位JDK(32位是x86),compiler2表示是Server VM(Client VM表示是compiler1)。
进入后可以见到以下几个目录。
0 drwxr-xr-x 15 IcyFenix staff 510B 12 13 17:24 debug
0 drwxr-xr-x 15 IcyFenix staff 510B 12 13 17:24 fastdebug
0 drwxr-xr-x 15 IcyFenix staff 510B 12 13 17:25 generated
0 drwxr-xr-x 15 IcyFenix staff 510B 12 13 17:24 jvmg
0 drwxr-xr-x 15 IcyFenix staff 510B 12 13 17:24 optimized
0 drwxr-xr-x 584 IcyFenix staff 19K 12 13 17:25 product
0 drwxr-xr-x 15 IcyFenix staff 510B 12 13 17:24 profiled
这些目录对应了不同的优化级别,优化级别越高,性能自然就越好,但是输出代码与源码的差距就越大,难于调试,具体哪个目录有内容,取决于make命令后面的参数。
运行虚拟机
在编译结束之后、运行虚拟机之前,还要手工编辑目录下的env.sh文件,这个文件由编译脚本自动产生,用于设置虚拟机的环境变量,里面已经发布了“JAVA_HOME、CLASSPATH、HOTSPOT_BUILD_USER”3个环境变量,还需要增加一个“LD_LIBRARY_PATH”,内容如下:
LD_LIBRARY_PATH=.:${JAVA_HOME}/jre/lib/amd64/native_threads:${JAVA_HOME}/jre/lib/amd64:
export LD_LIBRARY_PATH
然后执行以下命令启动虚拟机(这时的启动器名为gamma),输出版本号。
../env.sh
./gamma-version
Using java runtime at:/Library/Java/JavaVirtualMachines/jdk1.7.0_04.jdk/Contents/Home/jre
java version"1.7.0_04"
Java(TM)SE Runtime Environment(build 1.7.0_04-b21)
OpenJDK 64-BitServer VM(build 23.0-b21,mixed mode)