mirror of
https://github.com/fankes/moshi.git
synced 2025-10-20 00:19:21 +08:00
Call the kotlin compiler from within a test case.
This is a fragile first step.
This commit is contained in:
@@ -25,20 +25,10 @@
|
|||||||
<artifactId>moshi</artifactId>
|
<artifactId>moshi</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.squareup.moshi</groupId>
|
|
||||||
<artifactId>moshi-kotlin-codegen-runtime</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jetbrains.kotlin</groupId>
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
<artifactId>kotlin-stdlib</artifactId>
|
<artifactId>kotlin-stdlib</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>me.eugeniomarletti</groupId>
|
|
||||||
<artifactId>kotlin-metadata</artifactId>
|
|
||||||
<version>1.2.1</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.auto</groupId>
|
<groupId>com.google.auto</groupId>
|
||||||
<artifactId>auto-common</artifactId>
|
<artifactId>auto-common</artifactId>
|
||||||
@@ -65,6 +55,32 @@
|
|||||||
<artifactId>assertj-core</artifactId>
|
<artifactId>assertj-core</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
The Kotlin compiler must be near the end of the list because its .jar file includes an
|
||||||
|
obsolete version of Guava!
|
||||||
|
-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-compiler-embeddable</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-annotation-processing-embeddable</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>me.eugeniomarletti</groupId>
|
||||||
|
<artifactId>kotlin-metadata</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!--
|
||||||
|
Though we don't use compile-testing, including it is a convenient way to get tools.jar on the
|
||||||
|
classpath. This dependency is required by kapt3.
|
||||||
|
-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.testing.compile</groupId>
|
||||||
|
<artifactId>compile-testing</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@@ -129,6 +145,18 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>2.21.0</version>
|
||||||
|
<configuration>
|
||||||
|
<!--
|
||||||
|
Suppress the surefire classloader which prevents introspecting the classpath.
|
||||||
|
http://maven.apache.org/surefire/maven-surefire-plugin/examples/class-loading.html
|
||||||
|
-->
|
||||||
|
<useManifestOnlyJar>false</useManifestOnlyJar>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
@@ -168,7 +168,7 @@ internal class AdapterGenerator(
|
|||||||
if (property.differentiateAbsentFromNull) {
|
if (property.differentiateAbsentFromNull) {
|
||||||
result.beginControlFlow("%L -> ", index)
|
result.beginControlFlow("%L -> ", index)
|
||||||
result.addStatement("%N = %N.fromJson(%N)",
|
result.addStatement("%N = %N.fromJson(%N)",
|
||||||
property.localName, property.delegateName, readerParam);
|
property.localName, property.delegateName, readerParam)
|
||||||
result.addStatement("%N = true", property.localIsPresentName)
|
result.addStatement("%N = true", property.localIsPresentName)
|
||||||
result.endControlFlow()
|
result.endControlFlow()
|
||||||
} else {
|
} else {
|
||||||
|
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
|
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 CompilerTest {
|
||||||
|
@Rule @JvmField var temporaryFolder: TemporaryFolder = TemporaryFolder()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test() {
|
||||||
|
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 ConstructorParameters(var a: Int, var b: Int)
|
||||||
|
|""".trimMargin())
|
||||||
|
|
||||||
|
val result = call.execute()
|
||||||
|
assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR)
|
||||||
|
assertThat(result.systemErr).contains(
|
||||||
|
"@JsonClass can't be applied to ConstructorParameters: must be a Kotlin data class")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
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.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<String>()
|
||||||
|
val classpath = mutableListOf<String>()
|
||||||
|
val services = LinkedHashMultimap.create<KClass<*>, 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<String>()
|
||||||
|
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())
|
||||||
|
|
||||||
|
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<String> {
|
||||||
|
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<String> {
|
||||||
|
val result = mutableListOf<String>()
|
||||||
|
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<File> {
|
||||||
|
val classLoader = CompilerTest::class.java.classLoader
|
||||||
|
if (classLoader !is URLClassLoader) {
|
||||||
|
throw UnsupportedOperationException("unable to extract classpath from $classLoader")
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = mutableListOf<File>()
|
||||||
|
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 ")}}")
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
|
|
||||||
|
class KotlinCompilerResult(
|
||||||
|
val systemErr: String,
|
||||||
|
var exitCode: ExitCode
|
||||||
|
)
|
22
pom.xml
22
pom.xml
@@ -30,11 +30,13 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<java.version>1.7</java.version>
|
<java.version>1.7</java.version>
|
||||||
<kotlin.version>1.2.21</kotlin.version>
|
<kotlin.version>1.2.21</kotlin.version>
|
||||||
|
<kotlin-metadata.version>1.2.1</kotlin-metadata.version>
|
||||||
|
|
||||||
<!-- Dependencies -->
|
<!-- Dependencies -->
|
||||||
<okio.version>1.13.0</okio.version>
|
<okio.version>1.13.0</okio.version>
|
||||||
|
|
||||||
<!-- Test Dependencies -->
|
<!-- Test Dependencies -->
|
||||||
|
<compile-testing.version>0.8</compile-testing.version>
|
||||||
<junit.version>4.12</junit.version>
|
<junit.version>4.12</junit.version>
|
||||||
<assertj.version>1.7.0</assertj.version>
|
<assertj.version>1.7.0</assertj.version>
|
||||||
</properties>
|
</properties>
|
||||||
@@ -97,6 +99,26 @@
|
|||||||
<version>${kotlin.version}</version>
|
<version>${kotlin.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-compiler-embeddable</artifactId>
|
||||||
|
<version>${kotlin.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-annotation-processing-embeddable</artifactId>
|
||||||
|
<version>${kotlin.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>me.eugeniomarletti</groupId>
|
||||||
|
<artifactId>kotlin-metadata</artifactId>
|
||||||
|
<version>${kotlin-metadata.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.testing.compile</groupId>
|
||||||
|
<artifactId>compile-testing</artifactId>
|
||||||
|
<version>${compile-testing.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user