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