Skip to content

Commit a720e2f

Browse files
committed
adding CodeGenerator.group
The `group` value allows for basic scheduling of code generators. All code generators with the same group will be executed in a loop together until no new code is generated.
1 parent 43d580e commit a720e2f

File tree

9 files changed

+65
-93
lines changed

9 files changed

+65
-93
lines changed

CHANGELOG.md

+2-14
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
### Added
66

7+
- Added `CodeGenerator.group` for scheduling of code generators. All code generators with the same group will be executed in a loop together until no new code is generated.
8+
79
### Changed
810

911
- Anvil's generated hints are now all generated to the same `anvil.hint` package, which simplifies hint lookups and better future-proofs future KSP work. Note that this is a user-invisible change, but it will require a one-time recompilation of any Anvil-generated hints.
@@ -22,24 +24,10 @@
2224

2325
## [2.5.0-beta07] - 2024-04-16
2426

25-
### Added
26-
27-
### Changed
28-
29-
### Deprecated
30-
31-
### Removed
32-
3327
### Fixed
3428

3529
* Another mangled name workaround in KSP ([#966](https://github.com/square/anvil/pull/966))
3630

37-
### Security
38-
39-
### Custom Code Generator
40-
41-
### Other Notes & Contributions
42-
4331
## [2.5.0-beta06] - 2024-04-16
4432

4533
### Deprecated

compiler-api/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ After that implement the `CodeGenerator` interface:
4040
```kotlin
4141
@AutoService(CodeGenerator::class)
4242
class SampleCodeGenerator : CodeGenerator {
43+
44+
// execute before any of the default Anvil code generators (in group 0)
45+
override val group = -1
46+
4347
override fun isApplicable(context: AnvilContext): Boolean = true
4448

4549
override fun generateCode(

compiler-api/api/compiler-api.api

+11
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,21 @@ public abstract interface class com/squareup/anvil/compiler/api/AnvilContext {
4848
}
4949

5050
public abstract interface class com/squareup/anvil/compiler/api/CodeGenerator : com/squareup/anvil/compiler/api/AnvilApplicabilityChecker {
51+
public static final field Companion Lcom/squareup/anvil/compiler/api/CodeGenerator$Companion;
52+
public static final field GROUP_DEFAULT I
5153
public abstract fun generateCode (Ljava/io/File;Lorg/jetbrains/kotlin/descriptors/ModuleDescriptor;Ljava/util/Collection;)Ljava/util/Collection;
54+
public abstract fun getGroup ()I
5255
public abstract fun isApplicable (Lcom/squareup/anvil/compiler/api/AnvilContext;)Z
5356
}
5457

58+
public final class com/squareup/anvil/compiler/api/CodeGenerator$Companion {
59+
public static final field GROUP_DEFAULT I
60+
}
61+
62+
public final class com/squareup/anvil/compiler/api/CodeGenerator$DefaultImpls {
63+
public static fun getGroup (Lcom/squareup/anvil/compiler/api/CodeGenerator;)I
64+
}
65+
5566
public final class com/squareup/anvil/compiler/api/CodeGeneratorKt {
5667
public static final fun createGeneratedFile (Lcom/squareup/anvil/compiler/api/CodeGenerator;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/squareup/anvil/compiler/api/GeneratedFile;
5768
public static final fun createGeneratedFile (Lcom/squareup/anvil/compiler/api/CodeGenerator;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;[Ljava/io/File;)Lcom/squareup/anvil/compiler/api/GeneratedFileWithSources;

compiler-api/src/main/java/com/squareup/anvil/compiler/api/CodeGenerator.kt

+20
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ import java.io.File
1919
@ExperimentalAnvilApi
2020
public interface CodeGenerator : AnvilApplicabilityChecker {
2121

22+
/**
23+
* The group in which this generator will be invoked.
24+
*
25+
* All code generators with the same group will be executed in a loop together,
26+
* in ascending order of their fully qualified name, until no new code is generated.
27+
* Lower numbers will be executed first,
28+
* and will not execute again after their group has finished.
29+
*
30+
* The default group is 0.
31+
*/
32+
public val group: Int get() = 0
33+
2234
/**
2335
* Returns true if this code generator is applicable for the given [context] or false if not. This
2436
* will only be called _once_.
@@ -39,6 +51,14 @@ public interface CodeGenerator : AnvilApplicabilityChecker {
3951
module: ModuleDescriptor,
4052
projectFiles: Collection<KtFile>,
4153
): Collection<FileWithContent>
54+
55+
public companion object {
56+
/**
57+
* The default [group][CodeGenerator.group] for code generators.
58+
* A lower group value will be executed before any higher group value.
59+
*/
60+
public const val GROUP_DEFAULT: Int = 0
61+
}
4262
}
4363

4464
/**

compiler/src/main/java/com/squareup/anvil/compiler/codegen/CheckOnlyCodeGenerator.kt

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import java.io.File
1010
*/
1111
internal abstract class CheckOnlyCodeGenerator : PrivateCodeGenerator() {
1212

13+
override val group: Int get() = Int.MAX_VALUE
14+
1315
final override fun generateCodePrivate(
1416
codeGenDir: File,
1517
module: ModuleDescriptor,

compiler/src/main/java/com/squareup/anvil/compiler/codegen/CodeGenerationExtension.kt

+17-54
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import java.io.File
2929
import kotlin.LazyThreadSafetyMode.NONE
3030

3131
internal class CodeGenerationExtension(
32-
codeGenerators: List<CodeGenerator>,
32+
private val codeGenerators: List<CodeGenerator>,
3333
private val commandLineOptions: CommandLineOptions,
3434
private val moduleDescriptorFactory: RealAnvilModuleDescriptor.Factory,
3535
private val projectDir: BaseDir.ProjectDir,
@@ -56,18 +56,6 @@ internal class CodeGenerationExtension(
5656
private var didRecompile = false
5757
private var didSyncGeneratedDir = false
5858

59-
private val codeGenerators = codeGenerators
60-
.onEach {
61-
check(it !is FlushingCodeGenerator || it !is PrivateCodeGenerator) {
62-
"A code generator can't be a private code generator and flushing code generator at the " +
63-
"same time. Private code generators don't impact other code generators, therefore " +
64-
"they shouldn't need to flush files after other code generators generated code."
65-
}
66-
}
67-
// Use a stable sort in case code generators depend on the order.
68-
// At least don't make it random.
69-
.sortedWith(compareBy({ it is PrivateCodeGenerator }, { it::class.qualifiedName }))
70-
7159
override fun doAnalysis(
7260
project: Project,
7361
module: ModuleDescriptor,
@@ -196,11 +184,6 @@ internal class CodeGenerationExtension(
196184

197185
val generatedFiles = mutableMapOf<String, FileWithContent>()
198186

199-
val (privateCodeGenerators, nonPrivateCodeGenerators) =
200-
codeGenerators
201-
.filter { it.isApplicable(anvilContext) }
202-
.partition { it is PrivateCodeGenerator }
203-
204187
fun onGenerated(
205188
generatedFile: FileWithContent,
206189
codeGenerator: CodeGenerator,
@@ -237,43 +220,23 @@ internal class CodeGenerationExtension(
237220
.toKtFiles(psiManager, anvilModule)
238221
}
239222

240-
fun Collection<FlushingCodeGenerator>.flush(): List<KtFile> =
241-
flatMap { codeGenerator ->
242-
codeGenerator.flush(generatedDir, anvilModule)
243-
.onEach {
244-
onGenerated(
245-
generatedFile = it,
246-
codeGenerator = codeGenerator,
247-
// flushing code generators write the files but no content during normal rounds.
248-
allowOverwrites = true,
249-
)
250-
}
251-
.toKtFiles(psiManager, anvilModule)
252-
}
253-
254-
fun List<CodeGenerator>.loopGeneration() {
255-
var newFiles = generateAndCache(anvilModule.allFiles.toList())
256-
while (newFiles.isNotEmpty()) {
257-
// Parse the KtFile for each generated file. Then feed the code generators with the new
258-
// parsed files until no new files are generated.
259-
newFiles = generateAndCache(newFiles)
223+
// Group the code generators by their group number,
224+
// then sort them by their class name to keep the order stable.
225+
// All generators in a group will be looped together until none of them generate new files.
226+
codeGenerators
227+
.filter { it.isApplicable(anvilContext) }
228+
.groupBy { it.group }
229+
.entries
230+
.sortedBy { it.key }
231+
.map { (_, generators) -> generators.sortedBy { it::class.qualifiedName } }
232+
.forEach { generators ->
233+
var newFiles = generators.generateAndCache(anvilModule.allFiles.toList())
234+
while (newFiles.isNotEmpty()) {
235+
// Parse the KtFile for each generated file. Then feed the code generators with the new
236+
// parsed files until no new files are generated.
237+
newFiles = generators.generateAndCache(newFiles)
238+
}
260239
}
261-
}
262-
263-
// All non-private code generators are batched together.
264-
// They will execute against the initial set of files,
265-
// then loop until no generator produces any new files.
266-
nonPrivateCodeGenerators.loopGeneration()
267-
268-
// Flushing generators are next.
269-
// They have already seen all generated code.
270-
// Their output may be consumed by a private generator.
271-
codeGenerators.filterIsInstance<FlushingCodeGenerator>().flush()
272-
273-
// Private generators do not affect each other, so they're invoked last.
274-
// They may require multiple iterations of their own logic, though,
275-
// so we loop them individually until there are no more changes.
276-
privateCodeGenerators.forEach { listOf(it).loopGeneration() }
277240

278241
return generatedFiles.values
279242
}

compiler/src/main/java/com/squareup/anvil/compiler/codegen/ContributesSubcomponentHandlerGenerator.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.squareup.anvil.compiler.PARENT_COMPONENT
88
import com.squareup.anvil.compiler.SUBCOMPONENT_FACTORY
99
import com.squareup.anvil.compiler.SUBCOMPONENT_MODULE
1010
import com.squareup.anvil.compiler.api.AnvilContext
11+
import com.squareup.anvil.compiler.api.CodeGenerator
1112
import com.squareup.anvil.compiler.api.GeneratedFileWithSources
1213
import com.squareup.anvil.compiler.api.createGeneratedFile
1314
import com.squareup.anvil.compiler.contributesSubcomponentFactoryFqName
@@ -62,7 +63,9 @@ import java.io.File
6263
*/
6364
internal class ContributesSubcomponentHandlerGenerator(
6465
private val classScanner: ClassScanner,
65-
) : PrivateCodeGenerator() {
66+
) : CodeGenerator {
67+
68+
override val group: Int get() = 8
6669

6770
private val triggers = mutableListOf<Trigger>()
6871
private val contributions = mutableSetOf<Contribution>()
@@ -73,7 +76,7 @@ internal class ContributesSubcomponentHandlerGenerator(
7376

7477
override fun isApplicable(context: AnvilContext): Boolean = !context.generateFactoriesOnly
7578

76-
override fun generateCodePrivate(
79+
override fun generateCode(
7780
codeGenDir: File,
7881
module: ModuleDescriptor,
7982
projectFiles: Collection<KtFile>,

compiler/src/main/java/com/squareup/anvil/compiler/codegen/FlushingCodeGenerator.kt

-20
This file was deleted.

compiler/src/main/java/com/squareup/anvil/compiler/codegen/PrivateCodeGenerator.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import java.io.File
88

99
/**
1010
* Generates code that doesn't impact any other [CodeGenerator], meaning no other code generator
11-
* will process the generated code produced by this instance. A [PrivateCodeGenerator] is called
12-
* one last time after [FlushingCodeGenerator.flush] has been called to get a chance to evaluate
13-
* written results.
11+
* will process the generated code produced by this instance.
1412
*/
1513
internal abstract class PrivateCodeGenerator : CodeGenerator {
14+
15+
override val group: Int get() = 10
16+
1617
final override fun generateCode(
1718
codeGenDir: File,
1819
module: ModuleDescriptor,

0 commit comments

Comments
 (0)