はじめに
OpenJDK11 が 2018 年の 9 月にリリースされました。エンタープライズな案件で Java を使うとすると JDK11 が多いのではないでしょうか。
また、すでに JDK8 を使っているけど、新しく導入される一部のプロジェクトでは JDK11 を採用したい、という声もちらほら聞きます。
今回は JDK8 を使っているが JDK11 での検証を始めたときに、Gradle でのビルドを JDK8 と JDK11 の両方に対応したい(ビルドしたい)という場合に、どのようなビルド設定がよいのか考えました。なお JDK の変更にバージョンアップに伴うアプリケーションの修正、チューニングなどは、必要事項と思いますが、本記事からは除きます。あくまでビルド時に JDK を動的に切り替える方法に焦点を当てたいと思います。
設定内容
1. build.gradle
の sourceCompatibility
, targetCompatibility
でパラメータを指定
JDK のバージョンについては build.gradle
に固定値で指定していることが多いと思います。(あるいは gradle.properties
に記載)
以下のようなイメージです。
...
sourceCompatibility = 1.8
targetCompatibility = 1.8
...
あるいは gradle.properties
に値を記載しているとすると
...
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
...
...
javaVersion=1.8
...
というイメージです。
ビルド実行時に動的に切り替えたいので、 build.gradle
の定義は
...
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
...
と変数で定義しておきます。
2. ビルド時にパラメータで JDK のバージョンと JAVA_HOME
を設定
gradle
の -Pkey=value
でパラメータを指定できます。これを利用して、gradle build -PjavaVersion=1.11
などとすればビルド実行時に JDK のバージョンを指定できます。合わせて Gradle が用いる JDK を指定する必要があります。これは org.gradle.java.home
パラメータで指定することができます。
よって
gradle build -PjavaVersion=1.11 -Dorg.gradle.java.home="C:\java\11 --info"
のようにすれば良いのではないでしょうか。
確認
プロジェクト構成
以下のようなプロジェクト構成にしておきます。
C:
└─src
└─main
├─java
│ └─sample
│ └─gradle
│ └─multiversion
│ Sample.java
└─resources
plugins {
id 'java-library'
}
repositories {
jcenter()
}
dependencies {
}
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
JDK11 でビルド & 確認
JDK11 でビルドします。
>gradle build -PjavaVersion=1.11 -Dorg.gradle.java.home="C:\java\11"
BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed
実際にコンパイルされた class ファイルについて javap -v
でビルドされたバージョンを確認します。
以下の実行結果からわかるように major version: 55
と JDK11 でビルドされていることがわかります。
C:\workspace\sample-gradle-multiversion>javap -v build\classes\java\main\sample\gradle\multiversion\Sample.class
Classfile /C:/workspace/sample-gradle-multiversion/build/classes/java/main/sample/gradle/multiversion/Sample.class
Last modified 2019/07/12; size 576 bytes
MD5 checksum 580cd2384cb91a31c14e09339617ba75
Compiled from "Sample.java"
public class sample.gradle.multiversion.Sample
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #5 // sample/gradle/multiversion/Sample
super_class: #6 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #6.#20 // java/lang/Object."<init>":()V
#2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #23 // Hello world.
#4 = Methodref #24.#25 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #26 // sample/gradle/multiversion/Sample
#6 = Class #27 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lsample/gradle/multiversion/Sample;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 SourceFile
#19 = Utf8 Sample.java
#20 = NameAndType #7:#8 // "<init>":()V
#21 = Class #28 // java/lang/System
#22 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
#23 = Utf8 Hello world.
#24 = Class #31 // java/io/PrintStream
#25 = NameAndType #32:#33 // println:(Ljava/lang/String;)V
#26 = Utf8 sample/gradle/multiversion/Sample
#27 = Utf8 java/lang/Object
#28 = Utf8 java/lang/System
#29 = Utf8 out
#30 = Utf8 Ljava/io/PrintStream;
#31 = Utf8 java/io/PrintStream
#32 = Utf8 println
#33 = Utf8 (Ljava/lang/String;)V
{
public sample.gradle.multiversion.Sample();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lsample/gradle/multiversion/Sample;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello world.
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 10: 0
line 12: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
}
SourceFile: "Sample.java"
JDK 11 でビルドした class ファイルを削除するために gradle clean
しておきます。
C:\workspace\sample-gradle-multiversion>gradle clean -PjavaVersion=1.11
BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
JDK8 でビルド & 確認
次に JDK8 でビルドします。
C:\workspace\sample-gradle-multiversion>gradle build -PjavaVersion=1.8 -Dorg.gradle.java.home="C:\java\8"
BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed
class ファイルを確認します。 major version: 52
と JDK8 でビルドされていることがわかります。
C:\workspace\sample-gradle-multiversion>javap -v build\classes\java\main\sample\gradle\multiversion\Sample.class
Classfile /C:/workspace/sample-gradle-multiversion/build/classes/java/main/sample/gradle/multiversion/Sample.class
Last modified 2019/07/12; size 576 bytes
MD5 checksum ea23dd24700da60b773d93f8ff2f9d77
Compiled from "Sample.java"
public class sample.gradle.multiversion.Sample
minor version: 0
major version: 52
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #5 // sample/gradle/multiversion/Sample
super_class: #6 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #6.#20 // java/lang/Object."<init>":()V
#2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #23 // Hello world.
#4 = Methodref #24.#25 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #26 // sample/gradle/multiversion/Sample
#6 = Class #27 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lsample/gradle/multiversion/Sample;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 SourceFile
#19 = Utf8 Sample.java
#20 = NameAndType #7:#8 // "<init>":()V
#21 = Class #28 // java/lang/System
#22 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
#23 = Utf8 Hello world.
#24 = Class #31 // java/io/PrintStream
#25 = NameAndType #32:#33 // println:(Ljava/lang/String;)V
#26 = Utf8 sample/gradle/multiversion/Sample
#27 = Utf8 java/lang/Object
#28 = Utf8 java/lang/System
#29 = Utf8 out
#30 = Utf8 Ljava/io/PrintStream;
#31 = Utf8 java/io/PrintStream
#32 = Utf8 println
#33 = Utf8 (Ljava/lang/String;)V
{
public sample.gradle.multiversion.Sample();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lsample/gradle/multiversion/Sample;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello world.
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 10: 0
line 12: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
}
SourceFile: "Sample.java"
結論
-
build.gradle
のsourceCompatibility
,targetCompatibility
に変数で定義 - Gradleのビルド時にパラメータ
-P
で JDK のバージョンと-Dorg.gradle.java.home
でJAVA_HOME
を設定