From b7dc1e64b123df901c112c45ab18b817a2810003 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 17 Mar 2021 11:10:27 +0300 Subject: [PATCH] [KAPT] Disable JPMS when running annotation processing If sources contain module-info.java javac tries to validate modules existence/visibility during AP phase and fails, because we don't specify modules-path/patch-module. All these checks will be done in kotlin compiler and in javac for java classes. And it is not necessary to do it in AP too. So we go for easiest path possible - disable jpms for AP. #KT-32202 Fixed --- .../org/jetbrains/kotlin/gradle/Kapt3IT.kt | 39 +++++++++++++- .../kapt2/jpms-module/build.gradle | 51 +++++++++++++++++++ .../kapt2/jpms-module/gradle.properties | 1 + .../dagger_example/ApplicationComponent.java | 8 +++ .../java/dagger_example/ExampleModule.java | 13 +++++ .../src/main/java/dagger_example/Main.java | 20 ++++++++ .../java/dagger_example/OtherInjected.java | 6 +++ .../dagger_example/OtherInjectedImpl.java | 16 ++++++ .../src/main/java/module-info.java | 10 ++++ .../kotlin/dagger_example/InjectedClass.kt | 14 +++++ .../kotlin/dagger_example/SomeOtherClass.kt | 7 +++ .../jpms-module/src/main/kotlin/lab/test.kt | 15 ++++++ .../src/main/kotlin/test/ModuleUser.kt | 17 +++++++ .../kotlin/kapt3/base/KaptContext.kt | 14 +++-- .../kotlin/kapt3/base/annotationProcessing.kt | 17 +++++-- 15 files changed, 238 insertions(+), 10 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/build.gradle create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/gradle.properties create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/ApplicationComponent.java create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/ExampleModule.java create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/Main.java create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/OtherInjected.java create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/OtherInjectedImpl.java create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/module-info.java create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/dagger_example/InjectedClass.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/dagger_example/SomeOtherClass.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/lab/test.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/test/ModuleUser.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3IT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3IT.kt index 31f4f18a4a9..0e879ef8fc9 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3IT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3IT.kt @@ -676,4 +676,41 @@ open class Kapt3IT : Kapt3BaseIT() { assertContains("Javac options: {-source=1.8}") } } -} \ No newline at end of file + + @Test + fun testJpmsModule() { + //jpms is part of java >= 9 + val javaHome = File(System.getProperty("jdk9Home")!!) + Assume.assumeTrue("JDK 9 isn't available", javaHome.isDirectory) + val options = defaultBuildOptions().copy(javaHome = javaHome) + + val project = Project("jpms-module", directoryPrefix = "kapt2") + + project.build("build", options = options) { + assertSuccessful() + assertKaptSuccessful() + assertTasksExecuted(":compileKotlin", ":compileJava") + assertFileExists("build/generated/source/kapt/main/lab/TestClassGenerated.java") + assertFileExists(kotlinClassesDir() + "lab/TestClass.class") + } + + project.build("build", options = options) { + assertSuccessful() + assertTasksUpToDate(":compileKotlin", ":compileJava") + } + + project.projectDir.getFileByName("InjectedClass.kt").modify { text -> + text.checkedReplace( + "//placeholder", + "fun someChange() = null" + ) + } + + project.build("build", options = options) { + assertSuccessful() + assertKaptSuccessful() + assertTasksExecuted(":compileKotlin", ":compileJava") + + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/build.gradle new file mode 100644 index 00000000000..d9649af2a8e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/build.gradle @@ -0,0 +1,51 @@ +buildscript { + repositories { + mavenLocal() + mavenCentral() + gradlePluginPortal() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: "java" +apply plugin: "kotlin" +apply plugin: "kotlin-kapt" + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version" + kapt "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version" + + implementation 'com.google.dagger:dagger:2.33' + kapt 'com.google.dagger:dagger-compiler:2.33' + + //jpms module provider + implementation "org.apache.logging.log4j:log4j-api:2.14.0" +} + +compileKotlin.kotlinOptions.allWarningsAsErrors = true + +compileJava { + doFirst { + options.compilerArgs += [ + //workaround to make classpath into modules for java compilation so auto modules would work + '--module-path', classpath.asPath, + //provide compiled kotlin classes to javac (needed for java/kotlin mixed sources to work) + '--patch-module', "my.module=${sourceSets.main.output.asPath}" + ] + } +} + +//starting from gradle 6.4 it has built in jpms support +//still as for now it doesn't work with fully legacy jars (with no module's descriptor) +//so we need workaround above till we get AP packed as module +//java { +// modularity.inferModulePath.set(true) +//} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/gradle.properties new file mode 100644 index 00000000000..dc3793168f1 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/gradle.properties @@ -0,0 +1 @@ +kapt.verbose=true \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/ApplicationComponent.java b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/ApplicationComponent.java new file mode 100644 index 00000000000..d59cf433a6d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/ApplicationComponent.java @@ -0,0 +1,8 @@ +package dagger_example; + +import dagger.Component; + +@Component(modules = {ExampleModule.class}) +public interface ApplicationComponent { + void inject(Main main); +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/ExampleModule.java b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/ExampleModule.java new file mode 100644 index 00000000000..486f31c00e6 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/ExampleModule.java @@ -0,0 +1,13 @@ +package dagger_example; + +import dagger.Binds; +import dagger.Module; + +@Module +public abstract class ExampleModule { + @Binds + public abstract Injected bindInjected(InjectedImpl impl); + + @Binds + public abstract OtherInjected bindOtherInjected(OtherInjectedImpl impl); +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/Main.java b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/Main.java new file mode 100644 index 00000000000..7f92790843e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/Main.java @@ -0,0 +1,20 @@ +package dagger_example; + +import javax.inject.Inject; + +public class Main { + @Inject + Injected injected; + + @Inject + OtherInjected otherInjected; + + public static void main(String[] args) { + new Main().example(); + } + + private void example() { + DaggerApplicationComponent.create().inject(this); + System.out.println(injected.getMessage()); + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/OtherInjected.java b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/OtherInjected.java new file mode 100644 index 00000000000..d6abf227458 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/OtherInjected.java @@ -0,0 +1,6 @@ +package dagger_example; + +public interface OtherInjected { + + public String getMessage(); +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/OtherInjectedImpl.java b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/OtherInjectedImpl.java new file mode 100644 index 00000000000..0f0c8b4ada6 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/dagger_example/OtherInjectedImpl.java @@ -0,0 +1,16 @@ +package dagger_example; + +import javax.inject.Inject; + +public class OtherInjectedImpl implements OtherInjected { + + @Inject + public OtherInjectedImpl() { + + } + + @Override + public String getMessage() { + return "zzzz"; + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/module-info.java b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/module-info.java new file mode 100644 index 00000000000..4646c733e80 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/java/module-info.java @@ -0,0 +1,10 @@ +module my.module { + requires kotlin.stdlib; + requires org.apache.logging.log4j; + requires annotation.processor.example; + + requires dagger; + requires javax.inject; + //because dagger generates classes with @javax.annotation.processing.Generated + requires java.compiler; +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/dagger_example/InjectedClass.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/dagger_example/InjectedClass.kt new file mode 100644 index 00000000000..49d1b12e696 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/dagger_example/InjectedClass.kt @@ -0,0 +1,14 @@ +package dagger_example + +import javax.inject.Inject + +interface Injected { + + val message: String +} + +class InjectedImpl @Inject constructor() : Injected { + override val message = "This is injected1: " + SomeOtherClass().callMe() + + //placeholder +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/dagger_example/SomeOtherClass.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/dagger_example/SomeOtherClass.kt new file mode 100644 index 00000000000..60ec632e859 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/dagger_example/SomeOtherClass.kt @@ -0,0 +1,7 @@ +package dagger_example + +class SomeOtherClass { + + fun callMe() = "hell1o" + fun changeMe() = null +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/lab/test.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/lab/test.kt new file mode 100644 index 00000000000..e359a3d1d28 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/lab/test.kt @@ -0,0 +1,15 @@ +package lab + +@example.ExampleAnnotation +@example.ExampleSourceAnnotation +@example.ExampleBinaryAnnotation +@example.ExampleRuntimeAnnotation +public class TestClass { + + @example.ExampleAnnotation + public val testVal: String = "text" + + @example.ExampleAnnotation + public fun testFunction(): TestClassGenerated = TestClassGenerated() + +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/test/ModuleUser.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/test/ModuleUser.kt new file mode 100644 index 00000000000..f2c7064fd44 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/jpms-module/src/main/kotlin/test/ModuleUser.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package test + +import org.apache.logging.log4j.util.Base64Util +import org.apache.logging.log4j.* + +@example.ExampleAnnotation +class ModuleUser { + + fun useSomethingFromModule(): String = Base64Util.encode("test") + + fun haveSomeUsageInSignature(): Logger = TODO("I have nothing") +} diff --git a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/KaptContext.kt b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/KaptContext.kt index 264324babcb..8d8161af6b9 100644 --- a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/KaptContext.kt +++ b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/KaptContext.kt @@ -120,12 +120,10 @@ open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logge options.compileClasspath + options.compiledSources + options.classesOutputDir } - putJavacOption("CLASSPATH", "CLASS_PATH", - compileClasspath.joinToString(File.pathSeparator) { it.canonicalPath }) + putJavacOption("CLASSPATH", "CLASS_PATH", compileClasspath.makePathsString()) @Suppress("SpellCheckingInspection") - putJavacOption("PROCESSORPATH", "PROCESSOR_PATH", - options.processingClasspath.joinToString(File.pathSeparator) { it.canonicalPath }) + putJavacOption("PROCESSORPATH", "PROCESSOR_PATH", options.processingClasspath.makePathsString()) put(Option.S, options.sourcesOutputDir.canonicalPath) put(Option.D, options.classesOutputDir.canonicalPath) @@ -164,4 +162,10 @@ open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logge compiler.close() fileManager.close() } -} \ No newline at end of file + + companion object { + const val MODULE_INFO_FILE = "module-info.java" + + private fun Iterable.makePathsString(): String = joinToString(File.pathSeparator) { it.canonicalPath } + } +} diff --git a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/annotationProcessing.kt b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/annotationProcessing.kt index 7475a7f3129..b00181be0b5 100644 --- a/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/annotationProcessing.kt +++ b/plugins/kapt3/kapt3-base/src/org/jetbrains/kotlin/kapt3/base/annotationProcessing.kt @@ -38,9 +38,18 @@ fun KaptContext.doAnnotationProcessing( val wrappedProcessors = processors.map { ProcessorWrapper(it) } + val javaSourcesToProcess = run { + //module descriptor should be in root package, but here we filter it from everywhere (bc we don't have knowledge about root here) + val filtered = javaSourceFiles.filterNot { it.name == KaptContext.MODULE_INFO_FILE } + if (filtered.size != javaSourceFiles.size) { + logger.info("${KaptContext.MODULE_INFO_FILE} is removed from sources files to disable JPMS") + } + filtered + } + val compilerAfterAP: JavaCompiler try { - if (javaSourceFiles.isEmpty() && binaryTypesToReprocess.isEmpty() && additionalSources.isEmpty()) { + if (javaSourcesToProcess.isEmpty() && binaryTypesToReprocess.isEmpty() && additionalSources.isEmpty()) { if (logger.isVerbose) { logger.info("Skipping annotation processing as all sources are up-to-date.") } @@ -55,10 +64,10 @@ fun KaptContext.doAnnotationProcessing( } if (logger.isVerbose) { - logger.info("Processing java sources with annotation processors: ${javaSourceFiles.joinToString()}") + logger.info("Processing java sources with annotation processors: ${javaSourcesToProcess.joinToString()}") logger.info("Processing types with annotation processors: ${binaryTypesToReprocess.joinToString()}") } - val parsedJavaFiles = parseJavaFiles(javaSourceFiles) + val parsedJavaFiles = parseJavaFiles(javaSourcesToProcess) val sourcesStructureListener = cacheManager?.let { if (processors.any { it.isUnableToRunIncrementally() }) return@let null @@ -231,4 +240,4 @@ private fun KaptContext.initModulesIfNeeded(files: JavacList