diff --git a/kotlin/codegen/pom.xml b/kotlin/codegen/pom.xml index 566b578..21bda8e 100644 --- a/kotlin/codegen/pom.xml +++ b/kotlin/codegen/pom.xml @@ -25,7 +25,7 @@ com.squareup kotlinpoet - 1.3.0 + ${kotlinpoet.version} net.ltgt.gradle.incap @@ -37,12 +37,12 @@ com.google.auto auto-common - 0.10 + ${auto-common.version} com.google.auto.service auto-service-annotations - ${autoservice.version} + ${auto-service.version} provided true @@ -56,32 +56,31 @@ assertj-core test + + com.google.truth + truth + ${truth.version} + test + - - org.jetbrains.kotlin - kotlin-compiler-embeddable - test - - - org.jetbrains.kotlin - kotlin-annotation-processing-embeddable - test - me.eugeniomarletti.kotlin.metadata kotlin-metadata - - com.google.testing.compile - compile-testing + com.github.tschuchortdev + kotlin-compile-testing + ${kotlin-compile-testing.version} + test + + + com.squareup.okio + okio + ${okio2.version} test @@ -111,7 +110,7 @@ com.google.auto.service auto-service - ${autoservice.version} + ${auto-service.version} net.ltgt.gradle.incap diff --git a/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessorTest.kt b/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessorTest.kt index 99896ea..f60200c 100644 --- a/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessorTest.kt +++ b/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessorTest.kt @@ -15,384 +15,352 @@ */ package com.squareup.moshi.kotlin.codegen +import com.tschuchort.compiletesting.KotlinCompilation +import com.tschuchort.compiletesting.SourceFile +import com.tschuchort.compiletesting.SourceFile.Companion.kotlin import org.assertj.core.api.Assertions.assertThat -import org.jetbrains.kotlin.cli.common.ExitCode import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import javax.annotation.processing.Processor /** Execute kotlinc to confirm that either files are generated or errors are printed. */ class JsonClassCodegenProcessorTest { @Rule @JvmField var temporaryFolder: TemporaryFolder = TemporaryFolder() @Test fun privateConstructor() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |@JsonClass(generateAdapter = true) - |class PrivateConstructor private constructor(var a: Int, var b: Int) { - | fun a() = a - | fun b() = b - | companion object { - | fun newInstance(a: Int, b: Int) = PrivateConstructor(a, b) - | } - |} - |""".trimMargin()) - - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains("constructor is not internal or public") + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + + @JsonClass(generateAdapter = true) + class PrivateConstructor private constructor(var a: Int, var b: Int) { + fun a() = a + fun b() = b + companion object { + fun newInstance(a: Int, b: Int) = PrivateConstructor(a, b) + } + } + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains("constructor is not internal or public") } @Test fun privateConstructorParameter() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |@JsonClass(generateAdapter = true) - |class PrivateConstructorParameter(private var a: Int) - |""".trimMargin()) + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains("property a is not visible") + @JsonClass(generateAdapter = true) + class PrivateConstructorParameter(private var a: Int) + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains("property a is not visible") } @Test fun privateProperties() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |@JsonClass(generateAdapter = true) - |class PrivateProperties { - | private var a: Int = -1 - | private var b: Int = -1 - |} - |""".trimMargin()) + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains("property a is not visible") + @JsonClass(generateAdapter = true) + class PrivateProperties { + private var a: Int = -1 + private var b: Int = -1 + } + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains("property a is not visible") } @Test fun interfacesNotSupported() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |@JsonClass(generateAdapter = true) - |interface Interface - |""".trimMargin()) + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains( + @JsonClass(generateAdapter = true) + interface Interface + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains( "error: @JsonClass can't be applied to Interface: must be a Kotlin class") } - @Test fun abstractClassesNotSupported() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |@JsonClass(generateAdapter = true) - |abstract class AbstractClass(val a: Int) - |""".trimMargin()) + @Test fun interfacesDoNotErrorWhenGeneratorNotSet() { + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains( + @JsonClass(generateAdapter = true, generator="customGenerator") + interface Interface + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK) + } + + @Test fun abstractClassesNotSupported() { + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + + @JsonClass(generateAdapter = true) + abstract class AbstractClass(val a: Int) + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains( "error: @JsonClass can't be applied to AbstractClass: must not be abstract") } @Test fun sealedClassesNotSupported() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |@JsonClass(generateAdapter = true) - |sealed class SealedClass(val a: Int) - |""".trimMargin()) - - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains( + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + + @JsonClass(generateAdapter = true) + sealed class SealedClass(val a: Int) + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains( "error: @JsonClass can't be applied to SealedClass: must not be sealed") } @Test fun innerClassesNotSupported() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |class Outer { - | @JsonClass(generateAdapter = true) - | inner class InnerClass(val a: Int) - |} - |""".trimMargin()) - - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains( + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + + class Outer { + @JsonClass(generateAdapter = true) + inner class InnerClass(val a: Int) + } + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains( "error: @JsonClass can't be applied to Outer.InnerClass: must not be an inner class") } @Test fun enumClassesNotSupported() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |@JsonClass(generateAdapter = true) - |enum class KotlinEnum { - | A, B - |} - |""".trimMargin()) - - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains( + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + + @JsonClass(generateAdapter = true) + enum class KotlinEnum { + A, B + } + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains( "error: @JsonClass with 'generateAdapter = \"true\"' can't be applied to KotlinEnum: code gen for enums is not supported or necessary") } // Annotation processors don't get called for local classes, so we don't have the opportunity to // print an error message. Instead local classes will fail at runtime. - @Ignore - @Test fun localClassesNotSupported() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |fun outer() { - | @JsonClass(generateAdapter = true) - | class LocalClass(val a: Int) - |} - |""".trimMargin()) - - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains( + @Ignore @Test fun localClassesNotSupported() { + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + + fun outer() { + @JsonClass(generateAdapter = true) + class LocalClass(val a: Int) + } + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains( "error: @JsonClass can't be applied to LocalClass: must not be local") } @Test fun objectDeclarationsNotSupported() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |@JsonClass(generateAdapter = true) - |object ObjectDeclaration { - | var a = 5 - |} - |""".trimMargin()) - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains( + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + + @JsonClass(generateAdapter = true) + object ObjectDeclaration { + var a = 5 + } + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains( "error: @JsonClass can't be applied to ObjectDeclaration: must be a Kotlin class") } @Test fun objectExpressionsNotSupported() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |@JsonClass(generateAdapter = true) - |val expression = object : Any() { - | var a = 5 - |} - |""".trimMargin()) - - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains( + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + + @JsonClass(generateAdapter = true) + val expression = object : Any() { + var a = 5 + } + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains( "error: @JsonClass can't be applied to expression\$annotations(): must be a Kotlin class") } @Test fun requiredTransientConstructorParameterFails() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |@JsonClass(generateAdapter = true) - |class RequiredTransientConstructorParameter(@Transient var a: Int) - |""".trimMargin()) - - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains( + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + + @JsonClass(generateAdapter = true) + class RequiredTransientConstructorParameter(@Transient var a: Int) + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains( "error: No default value for transient property a") } @Test fun nonPropertyConstructorParameter() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |@JsonClass(generateAdapter = true) - |class NonPropertyConstructorParameter(a: Int, val b: Int) - |""".trimMargin()) - - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains( + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + + import com.squareup.moshi.JsonClass + @JsonClass(generateAdapter = true) + class NonPropertyConstructorParameter(a: Int, val b: Int) + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains( "error: No property for required constructor parameter a") } @Test fun badGeneratedAnnotation() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.kaptArgs[JsonClassCodegenProcessor.OPTION_GENERATED] = "javax.annotation.GeneratedBlerg" - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |@JsonClass(generateAdapter = true) - |data class Foo(val a: Int) - |""".trimMargin()) - - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains( + val result = prepareCompilation(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + + @JsonClass(generateAdapter = true) + data class Foo(val a: Int) + """ + )).apply { + kaptArgs[JsonClassCodegenProcessor.OPTION_GENERATED] = "javax.annotation.GeneratedBlerg" + }.compile() + assertThat(result.messages).contains( "Invalid option value for ${JsonClassCodegenProcessor.OPTION_GENERATED}") } @Test fun multipleErrors() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - | - |@JsonClass(generateAdapter = true) - |class Class1(private var a: Int, private var b: Int) - | - |@JsonClass(generateAdapter = true) - |class Class2(private var c: Int) - |""".trimMargin()) + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains("property a is not visible") - assertThat(result.systemErr).contains("property b is not visible") - assertThat(result.systemErr).contains("property c is not visible") + @JsonClass(generateAdapter = true) + class Class1(private var a: Int, private var b: Int) + + @JsonClass(generateAdapter = true) + class Class2(private var c: Int) + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains("property a is not visible") + assertThat(result.messages).contains("property c is not visible") } @Test fun extendPlatformType() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - |import java.util.Date - | - |@JsonClass(generateAdapter = true) - |class ExtendsPlatformClass(var a: Int) : Date() - |""".trimMargin()) + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + import java.util.Date - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains("supertype java.util.Date is not a Kotlin type") + @JsonClass(generateAdapter = true) + class ExtendsPlatformClass(var a: Int) : Date() + """ + )) + assertThat(result.messages).contains("supertype java.util.Date is not a Kotlin type") } @Test fun extendJavaType() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - |import com.squareup.moshi.kotlin.codegen.JavaSuperclass - | - |@JsonClass(generateAdapter = true) - |class ExtendsJavaType(var b: Int) : JavaSuperclass() - |""".trimMargin()) - - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr) + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + import com.squareup.moshi.kotlin.codegen.JavaSuperclass + + @JsonClass(generateAdapter = true) + class ExtendsJavaType(var b: Int) : JavaSuperclass() + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages) .contains("supertype com.squareup.moshi.kotlin.codegen.JavaSuperclass is not a Kotlin type") } - @Test - fun nonFieldApplicableQualifier() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - |import com.squareup.moshi.JsonQualifier - |import kotlin.annotation.AnnotationRetention.RUNTIME - |import kotlin.annotation.AnnotationTarget.PROPERTY - |import kotlin.annotation.Retention - |import kotlin.annotation.Target - | - |@Retention(RUNTIME) - |@Target(PROPERTY) - |@JsonQualifier - |annotation class UpperCase - | - |@JsonClass(generateAdapter = true) - |class ClassWithQualifier(@UpperCase val a: Int) - |""".trimMargin()) + @Test fun nonFieldApplicableQualifier() { + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + import com.squareup.moshi.JsonQualifier + import kotlin.annotation.AnnotationRetention.RUNTIME + import kotlin.annotation.AnnotationTarget.PROPERTY + import kotlin.annotation.Retention + import kotlin.annotation.Target - val result = call.execute() - println(result.systemErr) - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains("JsonQualifier @UpperCase must support FIELD target") + @Retention(RUNTIME) + @Target(PROPERTY) + @JsonQualifier + annotation class UpperCase + + @JsonClass(generateAdapter = true) + class ClassWithQualifier(@UpperCase val a: Int) + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains("JsonQualifier @UpperCase must support FIELD target") } - @Test - fun nonRuntimeQualifier() { - val call = KotlinCompilerCall(temporaryFolder.root) - call.inheritClasspath = true - call.addService(Processor::class, JsonClassCodegenProcessor::class) - call.addKt("source.kt", """ - |import com.squareup.moshi.JsonClass - |import com.squareup.moshi.JsonQualifier - |import kotlin.annotation.AnnotationRetention.BINARY - |import kotlin.annotation.AnnotationTarget.FIELD - |import kotlin.annotation.AnnotationTarget.PROPERTY - |import kotlin.annotation.Retention - |import kotlin.annotation.Target - | - |@Retention(BINARY) - |@Target(PROPERTY, FIELD) - |@JsonQualifier - |annotation class UpperCase - | - |@JsonClass(generateAdapter = true) - |class ClassWithQualifier(@UpperCase val a: Int) - |""".trimMargin()) + @Test fun nonRuntimeQualifier() { + val result = compile(kotlin("source.kt", + """ + import com.squareup.moshi.JsonClass + import com.squareup.moshi.JsonQualifier + import kotlin.annotation.AnnotationRetention.BINARY + import kotlin.annotation.AnnotationTarget.FIELD + import kotlin.annotation.AnnotationTarget.PROPERTY + import kotlin.annotation.Retention + import kotlin.annotation.Target - val result = call.execute() - assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) - assertThat(result.systemErr).contains("JsonQualifier @UpperCase must have RUNTIME retention") + @Retention(BINARY) + @Target(PROPERTY, FIELD) + @JsonQualifier + annotation class UpperCase + + @JsonClass(generateAdapter = true) + class ClassWithQualifier(@UpperCase val a: Int) + """ + )) + assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) + assertThat(result.messages).contains("JsonQualifier @UpperCase must have RUNTIME retention") } + + private fun prepareCompilation(vararg sourceFiles: SourceFile): KotlinCompilation { + return KotlinCompilation() + .apply { + workingDir = temporaryFolder.root + annotationProcessors = listOf(JsonClassCodegenProcessor()) + inheritClassPath = true + sources = sourceFiles.asList() + verbose = false + } + } + + private fun compile(vararg sourceFiles: SourceFile): KotlinCompilation.Result { + return prepareCompilation(*sourceFiles).compile() + } + } diff --git a/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/KotlinCompilerCall.kt b/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/KotlinCompilerCall.kt deleted file mode 100644 index 90a9567..0000000 --- a/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/KotlinCompilerCall.kt +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.squareup.moshi.kotlin.codegen - -import com.google.common.collect.LinkedHashMultimap -import okio.Buffer -import okio.Okio -import org.jetbrains.kotlin.cli.common.CLITool -import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler -import java.io.File -import java.io.FileOutputStream -import java.io.ObjectOutputStream -import java.io.PrintStream -import java.net.URLClassLoader -import java.net.URLDecoder -import java.util.zip.ZipEntry -import java.util.zip.ZipOutputStream -import kotlin.reflect.KClass - -/** Prepares an invocation of the Kotlin compiler. */ -class KotlinCompilerCall(var scratchDir: File) { - val sourcesDir = File(scratchDir, "sources") - val classesDir = File(scratchDir, "classes") - val servicesJar = File(scratchDir, "services.jar") - - var inheritClasspath = false - - val args = mutableListOf() - val kaptArgs = mutableMapOf() - val classpath = mutableListOf() - val services = LinkedHashMultimap.create, KClass<*>>() - - /** Adds a source file to be compiled. */ - fun addKt(path: String, source: String) { - val sourceFile = File(sourcesDir, path) - sourceFile.parentFile.mkdirs() - Okio.buffer(Okio.sink(sourceFile)).use { - it.writeUtf8(source) - } - } - - /** Adds a service like an annotation processor to make available to the compiler. */ - fun addService(serviceClass: KClass<*>, implementation: KClass<*>) { - services.put(serviceClass, implementation) - } - - fun execute(): KotlinCompilerResult { - val fullArgs = mutableListOf() - fullArgs.addAll(args) - - fullArgs.add("-d") - fullArgs.add(classesDir.toString()) - - val fullClasspath = fullClasspath() - if (fullClasspath.isNotEmpty()) { - fullArgs.add("-classpath") - fullArgs.add(fullClasspath.joinToString(separator = ":")) - } - - for (source in sourcesDir.listFiles()) { - fullArgs.add(source.toString()) - } - - fullArgs.addAll(annotationProcessorArgs()) - if (kaptArgs.isNotEmpty()) { - fullArgs.apply { - add("-P") - add("plugin:org.jetbrains.kotlin.kapt3:apoptions=${encodeOptions(kaptArgs)}") - } - } - - val systemErrBuffer = Buffer() - val oldSystemErr = System.err - System.setErr(PrintStream(systemErrBuffer.outputStream())) - try { - val exitCode = CLITool.doMainNoExit(K2JVMCompiler(), fullArgs.toTypedArray()) - val systemErr = systemErrBuffer.readUtf8() - return KotlinCompilerResult(systemErr, exitCode) - } finally { - System.setErr(oldSystemErr) - } - } - - /** Returns arguments necessary to enable and configure kapt3. */ - private fun annotationProcessorArgs(): List { - val kaptSourceDir = File(scratchDir, "kapt/sources") - val kaptStubsDir = File(scratchDir, "kapt/stubs") - - return listOf( - "-Xplugin=${kapt3Jar()}", - "-P", "plugin:org.jetbrains.kotlin.kapt3:sources=$kaptSourceDir", - "-P", "plugin:org.jetbrains.kotlin.kapt3:classes=$classesDir", - "-P", "plugin:org.jetbrains.kotlin.kapt3:stubs=$kaptStubsDir", - "-P", "plugin:org.jetbrains.kotlin.kapt3:apclasspath=$servicesJar", - "-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true" - ) - } - - /** Returns the classpath to use when compiling code. */ - private fun fullClasspath(): List { - val result = mutableListOf() - result.addAll(classpath) - - // Copy over the classpath of the running application. - if (inheritClasspath) { - for (classpathFile in classpathFiles()) { - result.add(classpathFile.toString()) - } - } - - if (!services.isEmpty) { - writeServicesJar() - result.add(servicesJar.toString()) - } - - return result.toList() - } - - /** - * Generate a .jar file that holds ServiceManager registrations. Necessary because AutoService's - * results might not be visible to this test. - */ - private fun writeServicesJar() { - ZipOutputStream(FileOutputStream(servicesJar)).use { zipOutputStream -> - for (entry in services.asMap()) { - zipOutputStream.putNextEntry( - ZipEntry("META-INF/services/${entry.key.qualifiedName}")) - val serviceFile = Okio.buffer(Okio.sink(zipOutputStream)) - for (implementation in entry.value) { - serviceFile.writeUtf8(implementation.qualifiedName!!) - serviceFile.writeUtf8("\n") - } - serviceFile.emit() // Don't close the entry; that closes the file. - zipOutputStream.closeEntry() - } - } - } - - /** Returns the files on the host process' classpath. */ - private fun classpathFiles(): List { - val classLoader = JsonClassCodegenProcessorTest::class.java.classLoader - if (classLoader !is URLClassLoader) { - throw UnsupportedOperationException("unable to extract classpath from $classLoader") - } - - val result = mutableListOf() - for (url in classLoader.urLs) { - if (url.protocol != "file") { - throw UnsupportedOperationException("unable to handle classpath element $url") - } - result.add(File(URLDecoder.decode(url.path, "UTF-8"))) - } - return result.toList() - } - - /** Returns the path to the kotlin-annotation-processing .jar file. */ - private fun kapt3Jar(): File { - for (file in classpathFiles()) { - if (file.name.startsWith("kotlin-annotation-processing-embeddable")) return file - } - throw IllegalStateException("no kotlin-annotation-processing-embeddable jar on classpath:\n " + - "${classpathFiles().joinToString(separator = "\n ")}}") - } - - /** - * Base64 encodes a mapping of annotation processor args for kapt, as specified by - * https://kotlinlang.org/docs/reference/kapt.html#apjavac-options-encoding - */ - private fun encodeOptions(options: Map): String { - val buffer = Buffer() - ObjectOutputStream(buffer.outputStream()).use { oos -> - oos.writeInt(options.size) - for ((key, value) in options.entries) { - oos.writeUTF(key) - oos.writeUTF(value) - } - } - return buffer.readByteString().base64() - } -} diff --git a/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/KotlinCompilerResult.kt b/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/KotlinCompilerResult.kt deleted file mode 100644 index cb4dba0..0000000 --- a/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/KotlinCompilerResult.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.squareup.moshi.kotlin.codegen - -import org.jetbrains.kotlin.cli.common.ExitCode - -class KotlinCompilerResult( - val systemErr: String, - var exitCode: ExitCode -) \ No newline at end of file diff --git a/pom.xml b/pom.xml index 62d5113..501a2e9 100644 --- a/pom.xml +++ b/pom.xml @@ -29,20 +29,25 @@ UTF-8 1.7 - 1.3.40 - 1.4.0 - 0.9.17 - 3.1.0 - 0.2 - 1.0-rc5 + 0.10 + 1.0-rc5 + 0.9.17 + 0.2 1.16.0 + 2.1.0 + 1.3.40 + 1.3.0 + 1.4.0 + 3.1.0 + 3.11.1 0.15 4.12 - 3.11.1 + 1.2.2 + 1.0