
4.2 从源码编译
相较于直接下载Graal VM JDK或Docker镜像,从源码编译则要复杂得多,此处以Linux平台为例进行介绍,基于撰写本节内容时的最新版本讲解。具体内容可能会随着项目的持续升级发生变化,读者可以从GraalVM在GitHub上的编译说明页面[1]查看最新的编译需求和方法。
4.2.1 编译准备
需要先确认编译所需的编译工具链都已安装在本地机器上:
- GCC 4.9.2;
- Make 3.83;
- Binutils 2.23.2;
- Cmake 3.6.1;
- Python 3.8。
编译还需要使用具有JVMCI功能的JDK——JVMCI[2](Java-Level JVM Compiler Inter-face),是从JDK9开始作为标准组件引入OpenJDK的,它是用Java语言编写JVM JIT编译器的接口。编译GraalVM所使用的基础JDK必须是具有JVMCI特性的。Oracle的GraalVM开发团队已经提供了graal-jvmci-8[3]和labs-openjdk-11[4]项目作为基础JDK项目。其中:graal-jvmci-8项目用于将高版本JDK才有的JVMCI特性移植到低版本JDK上;labs-openjdk-11则在上游的jvmci上有所增强,并增加了Bug修复。这两个基础JDK伴随OpenJDK的升级节奏按时发布新的版本,我们只需要从它们的发布页面上按需下载最新版本即可。
最后要准备好mx工具,这是基于Python的命令行编译开发框架工具,用于开发、编译和管理GraalVM项目,详见3.2节。
4.2.2 编译
当编译工具链、依赖组件和GraalVM的源码都准备好时,我们就可以开始编译GraalVM的源码了。假设graal-jvmci-8下载后解压到$OPENJDK8_JVMCI_HOME,mx项目源码下载后的根目录为$MX_HOME,GraalVM源码下载后的根目录是$GRAALVM_SOURCE,可以在$GRAALVM-SOURCE根目录下执行代码清单4-1中前3行命令编译GraalVM。
代码清单4-1 编译GraalVM源码命令
1 OPT="-v --primary-suite=vm --java-home=$OPENJDK8_JVMCI_HOME --dynamicimports /substratevm --disable-libpolyglot --disable-polyglot --force-bash-launchers=true --exclude-components=nju,nil,lg" 2 $MX_HOME/mx $OPT build 3 $MX_HOME/mx $OPT GraalVM-home //文件参数样例 4 DYNAMIC_IMPORTS=/substratevm 5 DISABLE_POLYGLOT=true 6 FORCE_BASH_LAUNCHERS=true 7 EXCLUDE_COMPONENTS=nju,nil,lg 8 $MX_HOME/mx --env sample build
接下来我们逐条介绍这些命令的含义。
1)代码清单4-1第1行设置了编译所需的参数。
① -v表示编译过程中打印出详细信息。
② --primary-suite=vm指出mx工具使用vm目录下的mx配置,如不设置此项也可以使用cd命令将当前工作环境切换到vm目录下,两者效果是相同的。
③ --java-home=$OPENJDK8_JVMCI_HOME指定了编译GraalVM所需的基础JDK目录,编译GraalVM用的默认JDK是环境变量$JAVA_HOME目录里的JDK,只有当希望使用另一个JDK时才需要用本参数设置,注意编译GraalVM必须使用具有JVMCI功能的JDK。
④ 再之后的参数都是设定编译内容的参数,表4-1中列出了设定编译内容的可用参数及其含义。
表4-1 mx编译GraalVM源码可用参数

mx支持3种方式的参数输入,分别是命令行参数、环境变量参数和文件参数,此处使用了命令行参数的形式。环境变量参数是将参数值设置到表4-1中对应的环境变量中,避免在编译时输入大段命令行参数;文件参数是将对环境变量参数的赋值保存在$GRAALVM_SOURCE/vm/mx.vm目录下的文件中,然后用--env指出该文件即可实现与代码清单4-1第1行相同的效果。例如将代码清单4-1中第4~7行的内容写到$GRAALVM_SOURCE/vm/mx.vm/sample文件中,然后运行第8行命令,会得到与执行第1、2行命令相同的效果。
2)代码清单4-1第2行执行编译,代码清单4-2中列出了按本例参数编译的GraalVM的所有组件。
代码清单4-2 GraalVM的编译内容清单
Components: - Polyglot Launcher ('poly', /polyglot) - Graal SDK ('sdk', /GraalVM) - LLVM.org toolchain ('llp', /llvm) - Truffle ('tfl', /truffle) - Truffle Macro ('tflm', /truffle) - Truffle NFI ('nfi', /nfi) - GraalVM compiler ('cmp', /graal) - SubstrateVM ('svm', /svm) - Native Image ('ni', /svm) - SubstrateVM LLVM ('svml', /svm) - Polyglot Native API ('polynative', /polyglot) - Native Image Configure Tool ('nic', /svm) - Component installer ('gu', /installer) - GraalVM license files ('gvm', /.) Launchers: - polyglot (bash) - native-image (bash) - native-image-configure (bash) - gu (bash) Libraries: - libnative-image-agent.so Installables: - LLVM_TOOLCHAIN_INSTALLABLE_JAVA8 - NATIVE_IMAGE_INSTALLABLE_JAVA8
3)代码清单4-1第3行可查看编译后的GraalVM JDK所在位置。编译的输出目录并不是固定目录,而是由GraalVM版本和编译组件等计算的SHA值确定的,在本例中的编译输出目录是$GRAALVM_SOURCE/sdk/mxbuild/linux-amd64/GRAALVM_ECB46508EE_JAVA8/GraalVM-ecb46508ee-java8-20.3.0-dev。我们将这个目录称为$GRAALVM_HOME,至此就完成了GraalVM源码的编译,得到了可执行的GraalVM JDK。
[1]参见https://github.com/oracle/graal/tree/master/vm#vm-suite。
[2]参见https://openjdk.java.net/jeps/243。