Skip to content

Commit

Permalink
Add option to disable the built-in processor.
Browse files Browse the repository at this point in the history
This allows us to change the default behavior of some annotations in case it the generated code doesn't provide the desired behavior.
  • Loading branch information
vRallev committed Sep 3, 2024
1 parent 50b136d commit e3d8f35
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 32 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,25 @@ A [sample project](sample) for Android and iOS is available.
The idea and more background about this library is covered in this
[public talk](https://ralf-wondratschek.com/presentation/extending-kotlin-inject.html).

## Advanced options

### Disabling processors

In some occasions the behavior of certain built-in symbol processors of kotlin-inject-anvil
doesn't meet expectations or should be changed. The recommendation in this case is to disable
the built-in processors and create your own. A processor can be disabled through KSP options, e.g.

```groovy
ksp {
arg("com.amazon.lastmile.kotlin.inject.anvil.processor.ContributesBindingProcessor", "disabled")
}
```

The key of the option must match the fully qualified name of the symbol processor and the value
must be `disabled`. All other values will keep the processor enabled. All built-in symbol
processors are part of
[this package](compiler/src/main/kotlin/com/amazon/lastmile/kotlin/inject/anvil/processor).

## Security

See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.KSAnnotated

internal class CompositeSymbolProcessor(
vararg symbolProcessors: SymbolProcessor,
symbolProcessors: Collection<SymbolProcessor>,
) : SymbolProcessor {

private val symbolProcessors = symbolProcessors.sortedBy { it::class.qualifiedName }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,53 @@ import com.google.devtools.ksp.processing.SymbolProcessorProvider
@Suppress("unused")
class KotlinInjectExtensionSymbolProcessorProvider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
return CompositeSymbolProcessor(
ContributesToProcessor(
codeGenerator = environment.codeGenerator,
logger = environment.logger,
),
ContributesBindingProcessor(
codeGenerator = environment.codeGenerator,
logger = environment.logger,
),
ContributesSubcomponentFactoryProcessor(
codeGenerator = environment.codeGenerator,
logger = environment.logger,
),
MergeComponentProcessor(
codeGenerator = environment.codeGenerator,
logger = environment.logger,
contributesSubcomponentProcessor = ContributesSubcomponentProcessor(
fun MutableSet<SymbolProcessor>.addIfEnabled(symbolProcessor: SymbolProcessor) {
val disabled = environment.options[symbolProcessor::class.qualifiedName] == "disabled"
if (!disabled) {
add(symbolProcessor)
} else {
environment.logger
.info("Disabled kotlin-inject-anvil processor ${symbolProcessor::class}")
}
}

val symbolProcessors = buildSet {
addIfEnabled(
ContributesToProcessor(
codeGenerator = environment.codeGenerator,
logger = environment.logger,
),
)
addIfEnabled(
ContributesBindingProcessor(
codeGenerator = environment.codeGenerator,
logger = environment.logger,
),
)
addIfEnabled(
ContributesSubcomponentFactoryProcessor(
codeGenerator = environment.codeGenerator,
logger = environment.logger,
),
contributingAnnotations = listOf(
ContributesTo::class,
ContributesBinding::class,
ContributesSubcomponent::class,
ContributesSubcomponent.Factory::class,
)
addIfEnabled(
MergeComponentProcessor(
codeGenerator = environment.codeGenerator,
logger = environment.logger,
contributesSubcomponentProcessor = ContributesSubcomponentProcessor(
codeGenerator = environment.codeGenerator,
logger = environment.logger,
),
contributingAnnotations = listOf(
ContributesTo::class,
ContributesBinding::class,
ContributesSubcomponent::class,
ContributesSubcomponent.Factory::class,
),
),
),
)
)
}

return CompositeSymbolProcessor(symbolProcessors)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ class Compilation internal constructor(
/**
* Configures the behavior of this compilation.
*/
fun configureAppPlatformProcessor(): Compilation = apply {
fun configureKotlinInjectAnvilProcessor(
processorOptions: Map<String, String> = emptyMap(),
): Compilation = apply {
checkNotCompiled()
check(!processorsConfigured) { "Processor should not be configured twice." }

Expand All @@ -43,6 +45,8 @@ class Compilation internal constructor(
SymbolProcessorProvider::class.java.classLoader,
)

this.processorOptions.putAll(processorOptions)

// Run KSP embedded directly within this kotlinc invocation
withCompilation = true
incremental = true
Expand Down Expand Up @@ -83,8 +87,9 @@ class Compilation internal constructor(
}

/**
* Compiles the underlying [KotlinCompilation]. Note that if [configureAppPlatformProcessor] has not been called
* prior to this, it will be configured with default behavior.
* Compiles the underlying [KotlinCompilation]. Note that if
* [configureKotlinInjectAnvilProcessor] has not been called prior to this, it will be
* configured with default behavior.
*/
fun compile(
@Language("kotlin") vararg sources: String,
Expand All @@ -93,7 +98,7 @@ class Compilation internal constructor(
checkNotCompiled()
if (!processorsConfigured) {
// Configure with default behaviors
configureAppPlatformProcessor()
configureKotlinInjectAnvilProcessor()
}
addSources(*sources)
isCompiled = true
Expand All @@ -119,7 +124,7 @@ class Compilation internal constructor(
* Helpful for testing code generators in unit tests end to end.
*
* This covers common cases, but is built upon reusable logic in [Compilation] and
* [Compilation.configureAppPlatformProcessor]. Consider using those APIs if more
* [Compilation.configureKotlinInjectAnvilProcessor]. Consider using those APIs if more
* advanced configuration is needed.
*/
fun compile(
Expand Down Expand Up @@ -149,7 +154,7 @@ fun compile(
addPreviousCompilationResult(previousCompilationResult)
}
}
.configureAppPlatformProcessor()
.configureKotlinInjectAnvilProcessor()
.compile(*sources)
.also {
if (exitCode == KotlinCompilation.ExitCode.OK) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@file:OptIn(ExperimentalCompilerApi::class)

package com.amazon.lastmile.kotlin.inject.anvil

import assertk.assertFailure
import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isInstanceOf
import com.tschuchort.compiletesting.KotlinCompilation.ExitCode.OK
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.junit.jupiter.api.Test

class KotlinInjectExtensionSymbolProcessorProviderTest {

@Test
fun `a processor can be disabled`() {
Compilation()
.configureKotlinInjectAnvilProcessor(
processorOptions = mapOf(
"com.amazon.lastmile.kotlin.inject.anvil.processor.ContributesToProcessor" to
"disabled",
),
)
.compile(
"""
package com.amazon.test
import com.amazon.lastmile.kotlin.inject.anvil.ContributesTo
@ContributesTo
@SingleInAppScope
interface ComponentInterface
""",
)
.run {
assertThat(exitCode).isEqualTo(OK)

// Throws the error because the generated component cannot be found.
assertFailure {
componentInterface.generatedComponent
}.isInstanceOf<ClassNotFoundException>()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class ContributesSubcomponentProcessorTest {
addPreviousCompilationResult(previousResult2)
addPreviousCompilationResult(previousResult3)
}
.configureAppPlatformProcessor()
.configureKotlinInjectAnvilProcessor()
.compile(
"""
package com.amazon.test
Expand Down

0 comments on commit e3d8f35

Please sign in to comment.