Gradle で モジュールの add-exports を指定する

blog1.mammb.com


add-exports / add-opens によるパッケージ公開

Java モジュールシステムにおいて、如何ともしがたい理由で、非公開のクラスを使いたいケースは、やはりたまに発生する。

その場合、コマンドライン引数で以下を指定することになる。

  • --add-exports <src-module>/<pkg-name>=<target-module>
    • 指定モジュールの指定パッケージの内部APIにアクセスする
  • --add-opens <src-module>/<pkg-name>=<<target.module>
    • 指定モジュールの指定パッケージの内部にリフレクションアクセスする


Gradle Kotlin DSL でコマンドライン引数を指定する

コンパイル、テスト実行、実行、およびJavaDoc時それぞれにコマンドライン引数を指定するには、build.gradle.kts で以下のスニペットを使用できる。

tasks.withType<JavaCompile>().configureEach {
    options.compilerArgs.addAll(arrayOf(
        "--add-exports", "jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED",
        "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"))
}

tasks.withType<Test>().configureEach {
    jvmArgs("--add-exports", "jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED",
            "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED")
}

tasks.withType<JavaExec>().configureEach {
    jvmArgs("--add-exports", "jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED",
            "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED")
}

tasks.withType<Javadoc>().configureEach {
    options {
        this as StandardJavadocDocletOptions
        addMultilineStringsOption("-add-exports").setValue(listOf(
            "jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED",
            "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"))
    }
}

それぞれ指定方法が異なるのでややこしい。

  • コンパイル時の compilerArgs にはコレクションを指定する
  • テスト/実行時の jvmArgs には可変長引数で指定する
  • JavaDoc には、addStringOption() では同一キーのオプションを複数登録できないため、addMultilineStringsOption() を使う
    • キー先頭の - は指定しない


オプションをまとめて定義する

以下のように定義することもできる。

val exportsOpt = arrayOf(
    "--add-exports", "jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED",
    "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED")

tasks.withType<JavaCompile>().configureEach {
    options.compilerArgs.addAll(exportsOpt)
}

tasks.withType<Test>().configureEach {
    jvmArgs(*exportsOpt)
}

tasks.withType<JavaExec>().configureEach {
    jvmArgs(*exportsOpt)
}

tasks.withType<Javadoc>().configureEach {
    options {
        this as StandardJavadocDocletOptions
        addMultilineStringsOption("-add-exports").setValue(
            exportsOpt.slice(1..exportsOpt.size step 2))
    }
}

jvmArgs() には可変長引数を渡すため spread operator で展開

addMultilineStringsOption() には値を取り出して設定


なお、アプリケーションプラグインでVMオプションを指定する場合は以下のように applicationDefaultJvmArgs に設定する。

application {
    applicationDefaultJvmArgs = listOf("...")
}


MANIFEST での Add-Exports 指定

Executable JAR にする場合は、MANIFEST.MF に Add-Exports を指定することで、実行時オプションが不要となる。

tasks.named<Jar>("jar") {
    manifest {
        attributes(
            "Main-Class" to application.mainClass,
            "Add-Exports" to "jdk.compiler/com.sun.tools.javac.jvm jdk.compiler/com.sun.tools.javac.api"
        )
    }
}

複数ある場合にはスペース区切りで記載する。 指定したものが、ターゲットモジュール ALL-UNNAMED で指定されたものとして扱われる。