Construire son propre JDK

Contexte

Les binaires du JDK sont disponible sur le site du JDK mais il est maintenu tant qu’une nouvelle version n’était pas sortie. Par exemple, lors de l’écriture de ce billet, c’est le JDK 15 qui est disponible. Il le restera tant que le JDK 16 n’est pas sortie. Ce dernier est disponible uniquement en accès anticipé.

Il existe aussi plusieurs fournisseurs dont votre distribution préférée si vous êtes sur Linux. Sinon, je vous conseille fortement, le projet AdoptOpenJDK initié par London Java Community.

Lors de mes conférences sur la migration Java 11 (Marseille, Bordeaux ou Paris), j’ai eu l’occassion de signaler qu’il était aussi possible de compiler son propre JDK puisque les sources sont disponibles en GPL.

Le déclic est venu lors des annonces de la JEP 358 Helpfull NullPointerException dans le JDK14, j’ai eu envie de tester moi-même cette fonctionnalité. Cependant, elle n’était pas encore intégrée dans une version en accès anticipé. Cela a été l’occassion de se lancer et d’écrire ce billet sur cette fonctionnalité. J’ai utilisé les diapositives de José Paumard qui a utilisé à Devoxx France 2019.

Maintenant, cela est encore plus simple depuis le projet Skara et la migration sur Git des sources du JDK. Plus besoin du client mercurial et des connaissances dessus. Si vous voulez plus d’informations sur le projet Skara et/ou la migration sur GIT

Environnement

Linux

Debianiste, je vais travailler naturellement sur mon environnement préféré : Debian Buster

Git

Le code source est disponible à l’adresse suivante : https://git.openjdk.java.net/jdk

github openjdk main line

Il suffira de choisir son dépôt. Nous prendrons le dépôt correspondant au prochain JDK, soit https://git.openjdk.java.net/jdk16.git

Outils de compilation

Une partie du JDK est écrit en C++, il est donc naturel d’avoir besoin des outils de compilation de Linux. Les outils sont les classiques sur votre distribution.

Nous le verrons, nous aurons besoin de certains librairies de dev pour réaliser les compilations. Ces packages sont reconnaissables car leur nom fini par -dev. Nous avons par exemple le package libx11-dev pour l’interface graphique X11. Elle contient notamment les en-têtes nécessaires pour la phase de compilation.

Le JDK utilise l’outil configure pour préparer la compilation et s’assurer que les pré-requis sont présents. Si cela n’est pas le cas, un message d’erreur sera affiché.

Autoconf is not found on the PATH (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin), and AUTOCONF is not set.
You need autoconf to be able to generate a runnable configure script.
You might be able to fix this by running 'sudo apt-get install autoconf'.
Error: Cannot find autoconf

L’erreur indique bien que l’outil autoconf n’a pas été trouvé. Il est nécessaire de l’installer avec la commande suivante :

apt-get install autoconf

JDK

Pour compiler un JDK, il faut un JDK. C’est ce que l’on appelle le "Boot JDK". Cela peut paraître paradoxalement, mais en réalité, c’est très logique. La plupart du code du JDK est du Java (cf le graphique ci-dessous)

jdk16 languages

Il est donc nécessaire d’avoir un JDK pour compiler le code Java.

Les étapes.

1. Préambule

J’utilise une VM Debian Buster minimale. Donc j’ai naturellement besoin d’installer l’outil Git et l’outil de compression Zip.

apt-get install git zip

2. Récupération des sources

Je passe ensuite à la récupération effective des sources :

cd /opt
mkdir sources
cd sources
git clone https://git.openjdk.java.net/jdk16.git

3. Récupération du JDK

Comme précisé plus haut, il est nécessaire d’avoir un JDK. Pour cela, il faut un JDK N-1 ou N-2 pour réaliser la compilation. Donc pour le JDK 16, je vais prendre un JDK 15 auprès du projet AdoptOpenJDK.

cd /opt
wget https://github.com/AdoptOpenJDK/openjdk15-binaries/releases/download/jdk-15.0.1%2B9/OpenJDK15U-jdk_x64_linux_hotspot_15.0.1_9.tar.gz
tar xzvf OpenJDK15U-jdk_x64_linux_hotspot_15.0.1_9.tar.gz
export JAVA_HOME=/opt/jdk-15.0.1+9/

4. Configuration des outils

avertissement Comme cité plus haut, vous pouvez utiliser l’outil configure pour vérifier les pré-requis et savoir ce qu’il vous manque. Ainsi, vous pouvez les installer au fur et à mesure. Dans ce chapitre, vous trouverez toute la liste selon les catégories.

Il faut passer maintenant à l’installation des outils de compilation Linux.

apt-get install autoconf build-essential

Nous installons aussi les libraires nécessaires

  • Librairies X11

apt-get install libx11-dev libxext-dev libxrender-dev libxrandr-dev libxtst-dev libxt-dev
  • Librairie CUPS (Imprimante)

apt-get install libcups2-dev
  • Librairie pour la configuration des polices

apt-get install libfontconfig1-dev
  • Librairie Alsa pour le son

apt-get install libasound2-dev

Tous les pré-requis sont là, vous pouvez exécuter la commande configure pour valider cette étape.

cd /opt/sources/jdk16
bash configure

Si tout est ok, vous obtenez un résumé de votre configuration

====================================================
A new configuration has been successfully created in
/opt/sources/jdk16/build/linux-x86_64-server-release
using default settings.

Configuration summary:
* Name:           linux-x86_64-server-release
* Debug level:    release
* HS debug level: product
* JVM variants:   server
* JVM features:   server: 'aot cds compiler1 compiler2 epsilongc g1gc graal jfr jni-check jvmci jvmti management nmt parallelgc serialgc services shenandoahgc vm-structs zgc'
* OpenJDK target: OS: linux, CPU architecture: x86, address length: 64
* Version string: 16-internal+0-adhoc.root.jdk16 (16-internal)

Tools summary:
* Boot JDK:       openjdk version "15.0.1" 2020-10-20 OpenJDK Runtime Environment AdoptOpenJDK (build 15.0.1+9) OpenJDK 64-Bit Server VM AdoptOpenJDK (build 15.0.1+9, mixed mode, sharing) (at /opt/jdk-15.0.1+9)
* Toolchain:      gcc (GNU Compiler Collection)
* C Compiler:     Version 8.3.0 (at /usr/bin/gcc)
* C++ Compiler:   Version 8.3.0 (at /usr/bin/g++)

Build performance summary:
* Cores to use:   1
* Memory limit:   1009 MB

avertissement Vous pourrez remarquer que j’ai pris une petite VM avec 1 Cpu et 1Go. La configuration recommandée est 2-4 coeurs et 2-4 Go (Plus vous avez des CPU, plus vous avez besoin de Mémoire)

5. Lancement de la compilation

Tout est bon, nous pouvons lancer la commande pour la compilation :

cd /opt/sources/jdk16
make images

Voici la sortie console de l’exécution de la commande.

Building target 'images' in configuration 'linux-x86_64-server-release'
Compiling 8 files for BUILD_TOOLS_LANGTOOLS
Compiling 16 properties into resource bundles for jdk.compiler
Parsing 2 properties into enum-like class for jdk.compiler
Compiling 13 properties into resource bundles for jdk.javadoc
Compiling 127 files for BUILD_java.compiler.interim
...
Creating jdk image
Creating CDS archive for jdk image
Creating CDS-NOCOOPS archive for jdk image
Updating images/sec-bin.zip
Stopping sjavac server
Finished building target 'images' in configuration 'linux-x86_64-server-release'

6. Vérification et utilisation

Ca y est, nous avons notre propre JDK. Il est disponible dans le répertoire build/linux-x86_64-server-release/images/jdk

root@buster:/opt/sources/jdk16/build/linux-x86_64-server-release/images/jdk# ls -al
total 44
drwxr-xr-x 10 root root 4096 Jan 10 12:42 .
drwxr-xr-x  6 root root 4096 Jan 10 12:42 ..
drwxr-xr-x  2 root root 4096 Jan 10 12:42 bin
drwxr-xr-x  5 root root 4096 Jan 10 12:42 conf
drwxr-xr-x  4 root root 4096 Jan 10 12:42 demo
drwxr-xr-x  3 root root 4096 Jan 10 12:42 include
drwxr-xr-x  2 root root 4096 Jan 10 12:42 jmods
drwxr-xr-x 72 root root 4096 Jan 10 12:42 legal
drwxr-xr-x  5 root root 4096 Jan 10 12:42 lib
drwxr-xr-x  3 root root 4096 Jan 10 12:42 man
-rw-r--r--  1 root root 1188 Jan 10 12:42 release

Il ne manque plus qu’à l’utiliser. Pour cela, nous allons démarrer la JVM avec l’option -version

bin/java -version
openjdk version "16-internal" 2021-03-16
OpenJDK Runtime Environment (build 16-internal+0-adhoc.root.jdk16)
OpenJDK 64-Bit Server VM (build 16-internal+0-adhoc.root.jdk16, mixed mode, sharing)

avertissement Pour réellement tester le nouveau JDK, vous pouvez exécuter la commande make run-test-tier1

7. Bonus

Nous avons utilisé la cible images en utilisant la commande :

make images

En réalité, il existe toute une série de cible de l’outil make. Celle-que j’aime bien, c’est la cible bootcycle-images

make bootcycles-images

Cela permet de construire les images deux fois, dont la seconde fois est réalisée avec le JDK construit lors de la première passe.