Skip to content

Commit 59c143c

Browse files
committed
Add more tests for AP, fix kapt crashing when no Kotlin sources are given
1 parent 3d2b49c commit 59c143c

File tree

7 files changed

+292
-144
lines changed

7 files changed

+292
-144
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ dependencies {
4949

5050
testCompile "me.eugeniomarletti.kotlin.metadata:kotlin-metadata:1.4.0"
5151
testCompile 'com.squareup:kotlinpoet:1.0.0-RC1'
52+
testCompile 'com.squareup:javapoet:1.11.1'
5253

5354
testImplementation "com.squareup.okio:okio:2.1.0"
5455
testImplementation 'io.github.classgraph:classgraph:4.6.10'

src/main/kotlin/com/tschuchort/compiletesting/Utils.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,6 @@ internal fun File.listFilesRecursively(): List<File> {
3333

3434
internal fun File.isKotlinFile()
3535
= listOf("kt", "kts").any{ it.equals(extension, ignoreCase = true) }
36+
37+
internal fun File.isJavaFile()
38+
= listOf("java").any{ it.equals(extension, ignoreCase = true) }

src/test/java/com/tschuchort/compiletesting/JavaTestProcessor.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.squareup.javapoet.JavaFile;
44
import com.squareup.kotlinpoet.FileSpec;
5-
import com.squareup.kotlinpoet.FunSpec;
65
import com.squareup.kotlinpoet.TypeSpec;
76
import kotlin.text.Charsets;
87

@@ -20,6 +19,11 @@
2019

2120
public class JavaTestProcessor extends AbstractProcessor {
2221

22+
public static String ON_INIT_MSG = "java processor init";
23+
public static String GENERATED_PACKAGE = "com.tschuchort.compiletesting";
24+
public static String GENERATED_JAVA_CLASS_NAME = "JavaGeneratedJavaClass";
25+
public static String GENERATED_KOTLIN_CLASS_NAME = "JavaGeneratedKotlinClass";
26+
2327
@Override
2428
public SourceVersion getSupportedSourceVersion() {
2529
return SourceVersion.latestSupported();
@@ -28,7 +32,7 @@ public SourceVersion getSupportedSourceVersion() {
2832
@Override
2933
public Set<String> getSupportedAnnotationTypes() {
3034
LinkedHashSet<String> set = new LinkedHashSet<>();
31-
set.add(Marker.class.getCanonicalName());
35+
set.add(ProcessElem.class.getCanonicalName());
3236
return set;
3337
}
3438

@@ -42,31 +46,29 @@ public Set<String> getSupportedOptions() {
4246
@Override
4347
public synchronized void init(ProcessingEnvironment processingEnv) {
4448
super.init(processingEnv);
45-
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "java processor init");
49+
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, ON_INIT_MSG);
4650
}
4751

4852
@Override
4953
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
5054
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "java processor was called");
5155

52-
if(!annotations.isEmpty()) {
53-
TypeSpec.Builder typeSpecBuilder = TypeSpec.classBuilder("JavaGeneratedKotlinClass");
56+
for (Element annotatedElem : roundEnv.getElementsAnnotatedWith(ProcessElem.class)) {
57+
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
58+
new ProcessedElemMessage(annotatedElem.getSimpleName().toString()).print());
59+
}
5460

55-
for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(Marker.class)) {
56-
typeSpecBuilder.addFunction(FunSpec.builder(annotatedElement.getSimpleName().toString()
57-
).build()
58-
).build();
59-
}
61+
if(annotations.isEmpty()) {
62+
TypeSpec.Builder typeSpecBuilder = TypeSpec.classBuilder(GENERATED_KOTLIN_CLASS_NAME);
6063

61-
FileSpec fileSpec = FileSpec.builder("com.tschuchort.compiletesting", "JavaGeneratedKotlinClass.kt")
62-
.addType(typeSpecBuilder.build())
63-
.build();
64+
FileSpec fileSpec = FileSpec.builder(GENERATED_PACKAGE, GENERATED_KOTLIN_CLASS_NAME + ".kt")
65+
.addType(typeSpecBuilder.build()).build();
6466

6567
writeKotlinFile(fileSpec, fileSpec.getName(), fileSpec.getPackageName());
6668

6769
try {
68-
JavaFile.builder("com.tschuchort.compiletesting",
69-
com.squareup.javapoet.TypeSpec.classBuilder("JavaGeneratedJavaClass").build())
70+
JavaFile.builder(GENERATED_PACKAGE,
71+
com.squareup.javapoet.TypeSpec.classBuilder(GENERATED_JAVA_CLASS_NAME).build())
7072
.build().writeTo(processingEnv.getFiler());
7173
} catch (Exception e) {
7274
}

src/test/kotlin/SmokeTests.kt

Lines changed: 0 additions & 95 deletions
This file was deleted.

src/test/kotlin/com/tschuchort/compiletesting/KotlinCompilation.kt

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
2727
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
2828
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
2929
import org.jetbrains.kotlin.config.Services
30+
import org.jetbrains.kotlin.incremental.isJavaFile
3031
import java.io.*
3132
import java.lang.RuntimeException
3233
import java.util.zip.ZipEntry
@@ -156,20 +157,23 @@ data class KotlinCompilation(
156157
// Directory for input source files
157158
private val sourcesDir get() = File(workingDir, "sources")
158159

160+
// Base directory for kapt stuff
161+
private val kaptDir get() = File(workingDir, "kapt")
162+
159163
// *.class files, Jars and resources (non-temporary) that are created by the
160164
// compilation will land here
161165
val classesDir get() = File(workingDir, "classes")
162166

163167
// Java annotation processors that are compile by kapt will put their generated files here
164-
val kaptSourceDir get() = File(workingDir, "kapt/sources")
168+
val kaptSourceDir get() = File(kaptDir, "sources")
165169

166170
// Output directory for Kotlin source files generated by kapt
167171
val kaptKotlinGeneratedDir get() = kaptArgs[OPTION_KAPT_KOTLIN_GENERATED]
168172
?.let { path ->
169173
require(File(path).isDirectory) { "$OPTION_KAPT_KOTLIN_GENERATED must be a directory" }
170174
File(path)
171175
}
172-
?: File(workingDir, "kapt/kotlinGenerated")
176+
?: File(kaptDir, "kotlinGenerated")
173177

174178

175179
/**
@@ -258,8 +262,9 @@ data class KotlinCompilation(
258262
/** Performs the 1st and 2nd compilation step to generate stubs and run annotation processors */
259263
private fun stubsAndApt(messageCollector: PrintingMessageCollector): ExitCode {
260264
// stubs and incrementalData are temporary
261-
val kaptStubsDir = File(workingDir, "kapt/stubs").apply { mkdirs() }
262-
val kaptIncrementalDataDir = File(workingDir, "kapt/incrementalData").apply { mkdirs() }
265+
val kaptStubsDir = File(kaptDir, "stubs").apply { mkdirs() }
266+
val kaptIncrementalDataDir = File(kaptDir, "incrementalData").apply { mkdirs() }
267+
kaptKotlinGeneratedDir.mkdirs()
263268

264269
require(kapt3Jar != null) { "kapt3Jar has to be non-null if annotation processing is used" }
265270

@@ -280,19 +285,38 @@ data class KotlinCompilation(
280285
// write unsafe context
281286
"plugin:org.jetbrains.kotlin.kapt3:aptMode=stubsAndApt",
282287
"plugin:org.jetbrains.kotlin.kapt3:mapDiagnosticLocations=true",
288+
"plugin:org.jetbrains.kotlin.kapt3:apoptions=$encodedKaptArgs",
283289
*if (verbose)
284290
arrayOf("plugin:org.jetbrains.kotlin.kapt3:verbose=true")
285-
else
286-
emptyArray(),
287-
*if (kaptArgs.isNotEmpty())
288-
arrayOf("plugin:org.jetbrains.kotlin.kapt3:apoptions=$encodedKaptArgs")
289291
else
290292
emptyArray()
291293
)
294+
295+
val kotlinSources = sourcesDir.listFilesRecursively().filter(File::isKotlinFile)
296+
val javaSources = sourcesDir.listFilesRecursively().filter(File::isJavaFile)
297+
298+
val allSourcePaths = mutableListOf<File>().apply {
299+
addAll(javaSources)
300+
301+
if(kotlinSources.isNotEmpty()) {
302+
addAll(kotlinSources)
303+
}
304+
else {
305+
/* __HACK__: The K2JVMCompiler expects at least one Kotlin source file or it will crash.
306+
We still need kapt to run even if there are no Kotlin sources because it executes APs
307+
on Java sources as well. Alternatively we could call the JavaCompiler instead of kapt
308+
to do annotation processing when there are only Java sources, but that's quite a lot
309+
of work (It can not be done in the compileJava step because annotation processors on
310+
Java files might generate Kotlin files which then need to be compiled in the
311+
compileKotlin step before the compileJava step). So instead we trick K2JVMCompiler
312+
by just including an empty .kt-File. */
313+
add(SourceFile("emptyKotlinFile.kt", "").writeTo(kaptDir))
314+
}
315+
316+
} .map(File::getAbsolutePath).distinct()
292317

293318
val k2JvmArgs = commonK2JVMArgs().also {
294-
it.freeArgs = sourcesDir.listFilesRecursively()
295-
.map(File::getAbsolutePath).distinct() + addKotlincArgs
319+
it.freeArgs = allSourcePaths + addKotlincArgs
296320

297321
it.pluginOptions = if(it.pluginOptions != null)
298322
it.pluginOptions!!.plus(kaptPluginOptions)
@@ -315,6 +339,7 @@ data class KotlinCompilation(
315339
kaptKotlinGeneratedDir.listFilesRecursively() +
316340
kaptSourceDir.listFilesRecursively())
317341

342+
// if no Kotlin sources are available, skip the compileKotlin step
318343
if(sources.filter<File>(File::isKotlinFile).isEmpty())
319344
return ExitCode.OK
320345

0 commit comments

Comments
 (0)