いけむランド

はてダからやってきました

Re: Gradle 6.7 で リリースされる Toolchain support for JVM projects を試してみる

この記事を参考に実際にやるとどんな感じなのかやってみたら、コンパイラ以外のツールは追従してなさそうだからちょっと困るのでは?という感じの結果になったため、メモっておきます。

progret.hatenadiary.com

今回は開発環境は 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 から追加されたクラスのようです。

github.com

issue 内のリンクを辿っていくと、どうもこの issue で話されている時点では forbidden-apis 側で対応するのは難しいという話になっています。

github.com

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 をインストールして、統一しておく方がとりあえずは良いのではないかと思います。