この記事を参考に実際にやるとどんな感じなのかやってみたら、コンパイラ以外のツールは追従してなさそうだからちょっと困るのでは?という感じの結果になったため、メモっておきます。
今回は開発環境は JDK の version 8 を使うけど、コンパイルは JDK の version 11 でしたいという状況を想定します。
とりあえず手元に JDK の version 8 と version 11 を用意します。(sdkman 経由で良いと思います。)
Gradle プロジェクトは Spring Initializr から適当に生成します。
取得したプロジェクトの build.gradle に toolchain support for JVM の設定と forbidden-apis プラグインを追加してみます。
plugins { id 'org.springframework.boot' version '2.3.4.RELEASE' id 'io.spring.dependency-management' version '1.0.10.RELEASE' id 'java' id 'de.thetaphi.forbiddenapis' version '3.1' // 追加 } // snip // 以下、追加 java { toolchain { languageVersion = JavaLanguageVersion.of(11) // Gradle は 8 で動かすけど、コンパイルは 11 で動かしたい } } forbiddenApis { bundledSignatures += 'jdk-system-out' }
(後述のエラーを発生させるために) ソースの方に文字列結合するコードを入れます。
public static void main(String[] args) { System.out.println("Class: " + DemoApplication.class.getName()); // 適当な文字列結合処理 SpringApplication.run(DemoApplication.class, args); }
実際にビルド (というか forbidden-apis による解析) してみます。
$ ./gradlew --version ------------------------------------------------------------ Gradle 6.7 ------------------------------------------------------------ Build time: 2020-10-14 16:13:12 UTC Revision: 312ba9e0f4f8a02d01854d1ed743b79ed996dfd3 Kotlin: 1.3.72 Groovy: 2.5.12 Ant: Apache Ant(TM) version 1.10.8 compiled on May 10 2020 JVM: 1.8.0_272 (Amazon.com Inc. 25.272-b10) OS: Mac OS X 10.15.7 x86_64 $ ./gradlew forbiddenApis > Task :forbiddenApisMain FAILED FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':forbiddenApisMain'. > de.thetaphi.forbiddenapis.ForbiddenApiException: Check for forbidden API calls failed while scanning class 'com.example.demo.DemoApplication' (DemoApplication.java): java.lang.ClassNotFoundException: java.lang.invoke.StringConcatFactory (while looking up details about referenced class 'java.lang.invoke.StringConcatFactory')
メッセージから察するに StringConcatFactory が見つからないようです。調べてみると、これは Java9 から追加されたクラスのようです。
issue 内のリンクを辿っていくと、どうもこの issue で話されている時点では forbidden-apis 側で対応するのは難しいという話になっています。
this can't be implemented for several reasons:
- there is no way to detect this from byte code without adding complex state machines to "understand the code".
- It won't work with Java 9 generated bytecode, as string concats no longer use StringBuilders behind the scenes.
この問題は他のツールでも起きそうな気がしています。というのもおそらく、ツールが動いている JDK の version と解析対象の class 群の version が異なるという想定をしていないツールが多いのではないかと思われるためです。(実際に目撃したものとしては、他のプロジェクトで PMD が UnsupportedClassVersionError で実行できませんでした。)
Gradle 6.7 はリリースされたばかりであるため、もしかしたらこれからツール側で対応が進むかもしれないため、それを待つのも手ですが、どうしても toolchain を使わないといけないという理由がないなら、toolchain を使わずに開発環境にもターゲットの version の JDK をインストールして、統一しておく方がとりあえずは良いのではないかと思います。