Skip to content

Commit

Permalink
Fix for Redeclaration errors due to KSP source set code generation
Browse files Browse the repository at this point in the history
Fixes evant#194
  • Loading branch information
eygraber committed May 6, 2022
1 parent 332f8e8 commit c890df3
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 10 deletions.
18 changes: 17 additions & 1 deletion integration-tests/module/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import org.gradle.configurationcache.extensions.capitalized

plugins {
id("kotlin-inject.multiplatform")
Expand All @@ -24,8 +25,23 @@ kotlin {
kotlin.sourceSets.commonMain {
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
}
kotlin.targets.configureEach {
kotlin.configureKsp(name)
}
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().configureEach {
if (name != "kspCommonMainKotlinMetadata") {
dependsOn("kspCommonMainKotlinMetadata")
}
}
}

fun org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetContainer.configureKsp(targetName: String) {
runCatching {
dependencies {
add("ksp${targetName.capitalized()}", project(":kotlin-inject-compiler:ksp"))
}

sourceSets.configureEach {
kotlin.srcDir("build/generated/ksp/$targetName/$name/kotlin")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ abstract class ExternalParentComponent {
}

@Component
abstract class ExternalChildComponent(@Component val parent: ExternalParentComponent = ExternalParentComponent::class.create())
abstract class ExternalChildComponent(@Component val parent: ExternalParentComponent = ExternalParentComponent::class.create())

expect abstract class PlatformComponent
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package me.tatarka.inject.test.module

import me.tatarka.inject.annotations.Component

@Component
actual abstract class PlatformComponent {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package me.tatarka.inject.test.module

import me.tatarka.inject.annotations.Component

@Component
actual abstract class PlatformComponent
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package me.tatarka.inject.test.module

import me.tatarka.inject.annotations.Component

@Component
actual abstract class PlatformComponent
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.Visibility
import com.squareup.kotlinpoet.ksp.writeTo
import com.squareup.kotlinpoet.ksp.kspDependencies
import com.squareup.kotlinpoet.ksp.originatingKSFiles
import me.tatarka.inject.compiler.COMPONENT
import me.tatarka.inject.compiler.PROVIDES
import me.tatarka.inject.compiler.FailedToGenerateException
import me.tatarka.inject.compiler.InjectGenerator
import me.tatarka.inject.compiler.Options
import me.tatarka.inject.compiler.PROVIDES
import me.tatarka.kotlin.ast.AstClass
import me.tatarka.kotlin.ast.KSAstProvider
import java.io.OutputStreamWriter
import java.nio.charset.StandardCharsets

class InjectProcessor(
private val options: Options,
Expand All @@ -47,7 +50,7 @@ class InjectProcessor(
with(provider) {
val astClass = element.toAstClass()
if (validate(element)) {
process(astClass)
process(astClass, element.inputSourceSet)
} else {
deferred.add(element)
}
Expand All @@ -57,10 +60,26 @@ class InjectProcessor(
return deferred
}

private fun process(astClass: AstClass) {
private fun process(astClass: AstClass, inputSourceSet: Any) {
try {
val file = generator.generate(astClass)
file.writeTo(codeGenerator, aggregating = true)
with(generator.generate(astClass)) {
val dependencies = kspDependencies(aggregating = true, originatingKSFiles())
val file = codeGenerator.createNewFile(dependencies, packageName, name)

// It needs to be after the call to CodeGenerator.createNewFile
val outputSourceSet = codeGenerator.generatedFile.first().toString().sourceSetBelow("ksp")

// TODO: This is a hack; https://github.com/google/ksp/issues/965
if (isInputAndOutputSourceSetsCompatible(inputSourceSet, outputSourceSet)) {
// Don't use writeTo(file) because that tries to handle directories under the hood
OutputStreamWriter(file, StandardCharsets.UTF_8)
.use(::writeTo)
} else {
logger.warn(
"Input and output source sets don't match (input=$inputSourceSet, output=$outputSourceSet)"
)
}
}
} catch (e: FailedToGenerateException) {
provider.error(e.message.orEmpty(), e.element)
// Continue so we can see all errors
Expand All @@ -72,13 +91,14 @@ class InjectProcessor(
for (element in deferred) {
with(provider) {
val astClass = element.toAstClass()
process(astClass)
process(astClass, element.inputSourceSet)
}
}
deferred = mutableListOf()
}

private fun validate(declaration: KSClassDeclaration): Boolean {
if (declaration.isExpect) return false
return declaration.accept(FixedKSValidateVisitor { node, _ ->
when (node) {
is KSFunctionDeclaration ->
Expand All @@ -92,6 +112,23 @@ class InjectProcessor(
}
}, null)
}

private val KSClassDeclaration.inputSourceSet
get() = containingFile?.filePath?.sourceSetBelow("src") ?: {
logger.error(
"Could not determine the source file for class '${(qualifiedName ?: simpleName).asString()}'"
)
"unknown"
}

private fun String.sourceSetBelow(startDirectoryName: String): String =
substringAfter("/$startDirectoryName/").substringBefore("/kotlin/").substringAfterLast('/')

private fun isInputAndOutputSourceSetsCompatible(inputSourceSet: Any, outputSourceSet: Any): Boolean {
return inputSourceSet == outputSourceSet ||
// heuristic that says the input probably isn't a multiplatform source
inputSourceSet is String && inputSourceSet.lowercase() == inputSourceSet
}
}

class InjectProcessorProvider : SymbolProcessorProvider {
Expand All @@ -102,4 +139,4 @@ class InjectProcessorProvider : SymbolProcessorProvider {
environment.logger
)
}
}
}

0 comments on commit c890df3

Please sign in to comment.