Getting started with GraalVM development

Introduction

The aim of this tutorial is to guide you through the process of setting up your working environment for editing, building, and testing GraalVM.

Prerequisites

This tutorial assumes that you will be working on Linux and you will be using IntelliJ IDEA as your IDE.

Bootstrapping the development environment

In order to work with GraalVM's source code you will need mx. To get a copy of it run:

git clone https://github.com/graalvm/mx.git

Don't forget to include the cloned repository in your PATH environment variable in order to be able to execute mx without specifying its absolute or relative path:

export PATH=$PATH:$PWD/mx

As expected you will also need to clone the GraalVM repository itself:

git clone https://github.com/oracle/graal.git

Now that you have both mx and the GraalVM repository cloned locally, you can start running mx commands that will help you complete the setup.

In order to build GraalVM you will also need to get a bootstrap JDK. To get a supported version you need to run:

mx fetch-jdk

This will prompt you to choose which supported JDK version you would like to use as the bootstrap JDK for your build, e.g.:

[1]*  labsjdk-ce-11             | ce-11.0.15+2-jvmci-22.1-b01
[2]   labsjdk-ce-11-debug       | ce-11.0.15+2-jvmci-22.1-b01
[3]   labsjdk-ce-17             | ce-17.0.3+2-jvmci-22.1-b01
[4]   labsjdk-ce-17-debug       | ce-17.0.3+2-jvmci-22.1-b01
[5]   Other version
Select JDK>

By default mx will install the fetched JDK under ~/.mx/jdks, if you want to install it in a different location you may use the --to or --alias flags:

  --to <dir>            location where JDK will be installed. Specify <system>
                        to use the system default location. (default:
                        ~/.mx/jdks)
  --alias <path>        name under which the extracted JDK should be made
                        available (e.g. via a symlink). A relative path will
                        be resolved against the value of the --to option.

Note, that mx will only fetch and install the JDK. It will not automatically set it as the current JDK. To do so, as prompted by mx, you will need to run the following to set JAVA_HOME in your shell:

export JAVA_HOME=~/.mx/jdks/labsjdk-ce-11-jvmci-22.1-b01

Now that JAVA_HOME is set we may run mx intellijinit to create the necessary files for IntelliJ IDEA to understand the project structure and resolve dependencies. mx intellijinit, however, may not be run from the root directory of the source code, so you will need to define a primary suit path for it to work, e.g.:

mx --primary-suite-path substratevm intellijinit

In general, most mx commands will require you to set the primary suite path. So depending on which part of GraalVM you are working on you will need to set the primary suite path accordingly. For native-image work you will need to set it to substratevm, for compiler to compiler, for truffle to truffle, and so forth.

Building GraalVM

To build the full GraalVM CE you need to run:

mx --primary-suite=vm --env=ce build

But it's highly unlikely you would need to build all the components that come with it, unless you want to release your build as a GraalVM distribution. For development and testing purposes you would probably need to build only a few of it's components. To see which components a command is going to build you can use graalvm-show, e.g.:

mx --primary-suite=vm --env=ce graalvm-show

Which will print something like the following:

GraalVM distribution: GRAALVM_B00B87AA07_JAVA11
Version: 22.1.0-dev
Config name: None
Components:
 - Component installer ('gu', /installer)
 - LLVM.org toolchain ('llp', /llvm)
 - Polyglot Launcher ('poly', /polyglot)
 - Native Image JUnit ('nju', /junit)
 - LibGraal ('lg', /False)
 - Native Image licence files ('nil', /svm)
 - GraalVM license files ('gvm', /.)
 - Truffle Macro ('tflm', /truffle)
 - Native Image Configure Tool ('nic', /svm)
 - Truffle ('tfl', /truffle)
 - Truffle NFI LIBFFI ('nfi-libffi', /nfi-libffi)
 - Native Image ('ni', /svm)
 - SubstrateVM LLVM ('svml', /svm)
 - Polyglot Native API ('polynative', /polyglot)
 - SVM Truffle NFI Support ('svmnfi', /nfi)
 - Native Image JUnit with image-builder on classpath ('njucp', /junitcp)
 - Graal SDK ('sdk', /graalvm)
 - SubstrateVM ('svm', /svm)
 - ICU4J ('icu4j', /truffle)
 - Truffle NFI ('nfi', /nfi)
 - GraalVM compiler ('cmp', /graal)
 - PolyBench Instruments ('pbi', /pbi)
 - Disassembler ('dis', /graal)
 - Polyglot Library ('libpoly', /polyglot)
Launchers:
 - gu (bash)
 - polyglot (bash)
 - native-image-configure (bash)
 - native-image (bash)
Libraries:
 - libjvmcicompiler.so (skipped)
 - libnative-image-agent.so (skipped)
 - libnative-image-diagnostics-agent.so (skipped)
 - libpolyglot.so (skipped)
Installables:
 - NATIVE_IMAGE_INSTALLABLE_SLIBNATIVE-IMAGE-AGENT.SO_SLIBNATIVE-IMAGE-DIAGNOSTICS-AGENT.SO_JAVA11
 - LLVM_TOOLCHAIN_INSTALLABLE_JAVA11
No standalone

So for instance in order to build a minimal substratevm (i.e., be able to generate native images) you can run:

mx --primary-suite-path substratevm --components='Native Image',nju build

which will build the following components:

$ mx --primary-suite-path substratevm --components='Native Image',nju graalvm-show
GraalVM distribution: GRAALVM_7B5A7CFCCC_JAVA17
Version: 22.1.0-dev
Config name: None
Components:
 - Native Image ('ni', /svm)
 - Native Image JUnit ('nju', /junit)
 - SubstrateVM ('svm', /svm)
 - Native Image licence files ('nil', /svm)
 - GraalVM compiler ('cmp', /graal)
 - Truffle Macro ('tflm', /truffle)
 - SVM Truffle NFI Support ('svmnfi', /nfi)
 - Truffle ('tfl', /truffle)
 - Truffle NFI ('nfi', /nfi)
 - Graal SDK ('sdk', /graalvm)
Launchers:
 - native-image (bash)
Libraries:
 - libnative-image-agent.so (skipped)
 - libnative-image-diagnostics-agent.so (skipped)
No installable
No standalone

The resulting build will be accessible under ./sdk/latest_graalvm_home, so to generate a native image one can run:

./sdk/latest_graalvm_home/bin/native-image Main

Now that you have bootstrapped your development environment and have successfully built GraalVM (or at least its substratevm) you are ready to open IntelliJ IDEA and start coding.

Testing your code

Once you are done coding, you will need to check whether your code breaks any of the existing tests or any of the ones you added in order to test your new code.

The easiest way to run the tests that come with GraalVM is by using the gate command. E.g.:

mx --primary-suite-path substratevm gate

Note that in order for gate to test your build (with the components you are interested in) instead of the default build, you will need to pass to mx the same parameters that you passed to it while building. E.g.:

mx --primary-suite-path substratevm --components='Native Image',nju gate

Style checks

Once you are done testing your code, you will need to check your code conforms to GraalVM's coding style. GraalVM is using two approaches to ensure the code is properly formatted. First there is checkstyle, which checks the code for style errors, and then there is eclipseformat which uses Eclipse to format the code.

checkstyle

To run checkstyle on your local workspace you need to run:

mx --primary-suite-path substratevm checkstyle

eclipseformat

To run checkstyle on your local workspace you need to run:

mx --primary-suite-path substratevm eclipseformat -e ~/.mx/eclipse/eclipse

eclipseformat doesn't always work with the eclipse version provided by the package manager of the system we are working on. As a result in some cases you will need to download and install the right Eclipse version and let mx know about its location through the -e parameter. E.g.:

mx --primary-suite-path substratevm eclipseformat -e ~/.mx/eclipse/eclipse

For more info about GraalVM please refer to the reference manual.

Foivos Zakkak
Foivos Zakkak
R&D Software Engineer

Foivos (pronounced [‘fivos]) Zakkak is a Software Engineer at Red Hat.