Introduce XFiler
The initial abstraction for XProcessing ignored the Filer
API since it is an interface. This CL changes it to have
an abstraction in XProcessing instead to avoid re-implementing
full Filer API in KSP.
Also added a test for code generation. Unfortunately, KSP
does not yet support access to generated code from java sources.
https://github.com/google/ksp/issues/119
Fortunately, it won't affect Room as we don't require access
to generated code.
Bug: 160322705
Test: XProcessingEnvTest.generateCode
Change-Id: I953a17cd857b7360d12269ccda1fe3c6b0489906
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XFiler.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XFiler.kt
new file mode 100644
index 0000000..dce852c
--- /dev/null
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XFiler.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.room.compiler.processing
+
+import com.squareup.javapoet.JavaFile
+
+/**
+ * Code generation interface for XProcessing.
+ */
+interface XFiler {
+ fun write(javaFile: JavaFile)
+}
+
+fun JavaFile.writeTo(generator: XFiler) = generator.write(this)
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XProcessingEnv.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XProcessingEnv.kt
index 11858cd..908cc4b 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XProcessingEnv.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XProcessingEnv.kt
@@ -18,7 +18,6 @@
import androidx.room.compiler.processing.javac.JavacProcessingEnv
import com.squareup.javapoet.TypeName
-import javax.annotation.processing.Filer
import javax.annotation.processing.ProcessingEnvironment
import kotlin.reflect.KClass
@@ -40,7 +39,7 @@
/**
* The API to generate files
*/
- val filer: Filer
+ val filer: XFiler
/**
* Looks for the [XTypeElement] with the given qualified name and returns `null` if it does not
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacFiler.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacFiler.kt
new file mode 100644
index 0000000..354236a
--- /dev/null
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacFiler.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.room.compiler.processing.javac
+
+import androidx.room.compiler.processing.XFiler
+import com.squareup.javapoet.JavaFile
+import javax.annotation.processing.Filer
+
+internal class JavacFiler(val filer: Filer) : XFiler {
+ override fun write(javaFile: JavaFile) {
+ javaFile.writeTo(filer)
+ }
+}
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnv.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnv.kt
index 753ebbe..746a4be 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnv.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnv.kt
@@ -50,7 +50,7 @@
JavacProcessingEnvMessager(delegate)
}
- override val filer = delegate.filer
+ override val filer = JavacFiler(delegate.filer)
override val options: Map<String, String>
get() = delegate.options
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFiler.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFiler.kt
new file mode 100644
index 0000000..e75da68
--- /dev/null
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFiler.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.room.compiler.processing.ksp
+
+import androidx.room.compiler.processing.XFiler
+import com.squareup.javapoet.JavaFile
+import org.jetbrains.kotlin.ksp.processing.CodeGenerator
+
+internal class KspFiler(
+ private val delegate: CodeGenerator
+) : XFiler {
+ override fun write(javaFile: JavaFile) {
+ delegate.createNewFile(
+ packageName = javaFile.packageName,
+ fileName = javaFile.typeSpec.name,
+ extensionName = "java"
+ ).use { outputStream ->
+ outputStream.bufferedWriter(Charsets.UTF_8).use {
+ javaFile.writeTo(it)
+ }
+ }
+ }
+}
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
index 079c176..9137b71 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
@@ -17,6 +17,7 @@
package androidx.room.compiler.processing.ksp
import androidx.room.compiler.processing.XArrayType
+import androidx.room.compiler.processing.XFiler
import androidx.room.compiler.processing.XDeclaredType
import androidx.room.compiler.processing.XMessager
import androidx.room.compiler.processing.XProcessingEnv
@@ -30,14 +31,14 @@
import org.jetbrains.kotlin.ksp.symbol.KSType
import org.jetbrains.kotlin.ksp.symbol.KSTypeReference
import org.jetbrains.kotlin.ksp.symbol.Variance
-import javax.annotation.processing.Filer
internal class KspProcessingEnv(
override val options: Map<String, String>,
- private val codeGenerator: CodeGenerator,
+ codeGenerator: CodeGenerator,
private val logger: KSPLogger,
val resolver: Resolver
) : XProcessingEnv {
+
private val typeElementStore =
XTypeElementStore { qName ->
resolver.getClassDeclarationByName(
@@ -53,8 +54,7 @@
override val messager: XMessager
get() = TODO("Not yet implemented")
- override val filer: Filer
- get() = TODO("Not yet implemented")
+ override val filer: XFiler = KspFiler(codeGenerator)
val commonTypes = CommonTypes(resolver)
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
index 1da78f89c..6c8efc0 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
@@ -17,14 +17,18 @@
package androidx.room.compiler.processing
import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.TestInvocation
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import javax.lang.model.element.Modifier
@RunWith(JUnit4::class)
class XProcessingEnvTest {
@@ -166,6 +170,50 @@
}
}
+ @Test
+ fun generateCode() {
+ val javaSrc = Source.java(
+ "foo.bar.AccessGenerated",
+ """
+ package foo.bar;
+ public class AccessGenerated {
+ ToBeGenerated x;
+ }
+ """.trimIndent()
+ )
+ val kotlinSrc = Source.kotlin(
+ "AccessGenerated.kt",
+ """
+ package foo.bar;
+ public class AccessGenerated(x: ToBeGenerated)
+ """.trimIndent()
+ )
+ listOf(javaSrc, kotlinSrc).forEach { src ->
+ fun runTest(block: (TestInvocation) -> Unit) {
+ // KSP does not support generated code access in java sources yet
+ // TODO remove this check once the bug is fixed.
+ // https://github.com/google/ksp/issues/119
+ if (src === javaSrc) {
+ runProcessorTest(sources = listOf(src), block)
+ } else {
+ runProcessorTestIncludingKsp(sources = listOf(src), block)
+ }
+ }
+ runTest { invocation ->
+ val className = ClassName.get("foo.bar", "ToBeGenerated")
+ if (invocation.processingEnv.findTypeElement(className) == null) {
+ // generate only if it doesn't exist to handle multi-round
+ val spec = TypeSpec.classBuilder(className)
+ .addModifiers(Modifier.PUBLIC)
+ .build()
+ JavaFile.builder(className.packageName(), spec)
+ .build()
+ .writeTo(invocation.processingEnv.filer)
+ }
+ }
+ }
+ }
+
companion object {
val PRIMITIVE_TYPES = mapOf(
TypeName.BOOLEAN.toString() to TypeName.BOOLEAN,
diff --git a/room/compiler/src/main/kotlin/androidx/room/writer/ClassWriter.kt b/room/compiler/src/main/kotlin/androidx/room/writer/ClassWriter.kt
index 77cbf82..23b04c1 100644
--- a/room/compiler/src/main/kotlin/androidx/room/writer/ClassWriter.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/writer/ClassWriter.kt
@@ -20,6 +20,7 @@
import androidx.room.ext.S
import androidx.room.ext.typeName
import androidx.room.compiler.processing.XProcessingEnv
+import androidx.room.compiler.processing.writeTo
import androidx.room.solver.CodeGenScope.Companion.CLASS_PROPERTY_PREFIX
import com.squareup.javapoet.AnnotationSpec
import com.squareup.javapoet.ClassName
diff --git a/room/compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt b/room/compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt
index 425a078..4096f31 100644
--- a/room/compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt
@@ -18,6 +18,7 @@
import androidx.room.compiler.processing.XProcessingEnv
import androidx.room.compiler.processing.XType
+import androidx.room.compiler.processing.writeTo
import androidx.room.processor.Context
import androidx.room.testing.TestInvocation
import com.squareup.javapoet.ClassName
@@ -152,7 +153,9 @@
.build()
)
.build()
- JavaFile.builder("foo.bar", spec).build().writeTo(invocation.processingEnv.filer)
+ JavaFile.builder("foo.bar", spec).build().writeTo(
+ invocation.processingEnv.filer
+ )
}
@Test