GraalVM与Java静态编译:原理与应用
上QQ阅读APP看书,第一时间看更新

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源码可用参数

051-01

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

[3]参见https://github.com/GraalVM/graal-jvmci-8

[4]参见https://github.com/GraalVM/labs-openjdk-11