Skip to content

Commit 78b1b11

Browse files
authored
Merge pull request #299 from square/ralf/docs
Add documentation for `CodeGenerator`s.
2 parents 4b77b67 + f8f0b78 commit 78b1b11

File tree

4 files changed

+138
-36
lines changed

4 files changed

+138
-36
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 2.3.0
4+
5+
* Add option to extend Anvil with your own `CodeGenerator`, see [here](README.md#extending-anvil) and #265.
6+
* Use Gradle Property APIs in the Anvil extension. This is a source-breaking change (but binary-compatible) for Kotlin and .kts consumers of the Anvil Gradle plugin, see #284.
7+
* Upgrade Anvil to Kotlin `1.5.10`. The old legacy compiler backend is still supported and the IR backend not required yet.
8+
39
## 2.2.3 (2021-05-25)
410

511
* Support the JVM and Android targets for Kotlin Multiplatform projects, see #222.

README.md

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,48 @@ wrong imports and deeply nested dependency chains applications might need to mak
201201
exclusion rule does what it implies. In this specific example `DaggerModule` wishes to be
202202
contributed to this scope, but it has been excluded for this component and thus is not added.
203203

204-
## Advantages
204+
## Dagger Factory Generation
205+
206+
Anvil allows you to generate Factory classes that usually the Dagger annotation processor would
207+
generate for `@Provides` methods, `@Inject` constructors and `@Inject` fields. The benefit of this
208+
feature is that you don't need to enable the Dagger annotation processor in this module. That often
209+
means you can skip KAPT and the stub generating task. In addition Anvil generates Kotlin instead
210+
of Java code, which allows Gradle to skip the Java compilation task. The result is faster
211+
builds.
212+
213+
```groovy
214+
anvil {
215+
generateDaggerFactories = true // default is false
216+
}
217+
```
218+
219+
In our codebase we measured that modules using Dagger build 65% faster with this new Anvil feature
220+
compared to using the Dagger annotation processor:
221+
222+
| Stub generation | Kapt | Javac | Kotlinc | Sum
223+
:--- | ---: | ---: | ---: | ---: | ---:
224+
Dagger | 12.976 | 40.377 | 8.571 | 10.241 | 72.165
225+
Anvil | 0 | 0 | 6.965 | 17.748 | 24.713
226+
227+
For full builds of applications we measured savings of 16% on average.
228+
229+
![Benchmark Dagger Factories](images/benchmark_dagger_factories.png?raw=true "Benchmark Dagger Factories")
230+
231+
This feature can only be enabled in Gradle modules that don't compile any Dagger component. Since
232+
Anvil only processes Kotlin code, you shouldn't enable it in modules with mixed Kotlin / Java
233+
sources either.
234+
235+
When you enable this feature, don't forget to remove the Dagger annotation processor. You should
236+
keep all other dependencies.
237+
238+
## Extending Anvil
239+
240+
Every codebase has its own dependency injection patterns where certain code structures need to be
241+
repeated over and over again. Here Anvil comes to the rescue and you can extend the compiler
242+
plugin with your own `CodeGenerator`. For usage please take a look at the
243+
[`compiler-api` artifact](compiler-api/README.md)
244+
245+
## Advantages of Anvil
205246

206247
Adding Dagger modules to components in a large modularized codebase with many application targets
207248
is overhead. You need to know where components are defined when creating a new Dagger module and
@@ -236,6 +277,9 @@ compile tasks are skipped entirely, if nothing has changed. This doesn't change
236277

237278
![Benchmark](images/benchmark.png?raw=true "Benchmark")
238279

280+
On top of that, Anvil provides actual build time improvements by replacing the Dagger annotation
281+
processor in many modules if you enable [Dagger Factory generation](#dagger-factory-generation).
282+
239283
## Kotlin compiler plugin
240284

241285
We investigated whether other alternatives like a bytecode transformer and an annotation processor
@@ -276,40 +320,6 @@ for performance reasons. With Hilt we wouldn't be able to enforce this requireme
276320
component interfaces. The development of Anvil started long before Hilt was announced and the
277321
internal version is being used in production for a while.
278322

279-
## Dagger Factory generation
280-
281-
Anvil allows you to generate Factory classes that usually the Dagger annotation processor would
282-
generate for `@Provides` methods, `@Inject` constructors and `@Inject` fields. The benefit of this
283-
feature is that you don't need to enable the Dagger annotation processor in this module. That often
284-
means you can skip KAPT and the stub generating task. In addition Anvil generates Kotlin instead
285-
of Java code, which allows Gradle to skip the Java compilation task. The result is faster
286-
builds.
287-
288-
```groovy
289-
anvil {
290-
generateDaggerFactories = true // default is false
291-
}
292-
```
293-
294-
In our codebase we measured that modules using Dagger build 65% faster with this new Anvil feature
295-
compared to using the Dagger annotation processor:
296-
297-
  | Stub generation | Kapt | Javac | Kotlinc | Sum
298-
:--- | ---: | ---: | ---: | ---: | ---:
299-
Dagger | 12.976 | 40.377 | 8.571 | 10.241 | 72.165
300-
Anvil | 0 | 0 | 6.965 | 17.748 | 24.713
301-
302-
For full builds of applications we measured savings of 16% on average.
303-
304-
![Benchmark Dagger Factories](images/benchmark_dagger_factories.png?raw=true "Benchmark Dagger Factories")
305-
306-
This feature can only be enabled in Gradle modules that don't compile any Dagger component. Since
307-
Anvil only processes Kotlin code, you shouldn't enable it in modules with mixed Kotlin / Java
308-
sources either.
309-
310-
When you enable this feature, don't forget to remove the Dagger annotation processor. You should
311-
keep all other dependencies.
312-
313323
## License
314324

315325
Copyright 2020 Square, Inc.

compiler-api/README.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Compiler-API
2+
3+
Anvil is composed of several independent code generators that provide hints for the later stages
4+
in the build graph when all modules and components are merged together. These code generators
5+
are similar to annotation processors in the way that they look for a specific annotation like
6+
`@ContributesTo` and then generate necessary code.
7+
8+
If you see yourself repeating the same code structures and patterns for your dependency injection
9+
setup over and over again, then you can extend Anvil and implement your own code generator via the `CodeGenerator` interface.
10+
11+
## Steps
12+
It's recommended to create your own annotation for your use case to not conflict with Anvil and
13+
its own annotations. The annotation needs to live in its own module separate from the code
14+
generator, e.g. `:sample:annotation`.
15+
```kotlin
16+
annotation class MyAnnotation
17+
```
18+
Later you'll use this annotation to give your code generator a hint for work to perform.
19+
20+
In the code generator module `:sample:code-generator` you need to import the `compiler-api`
21+
artifact:
22+
```groovy
23+
dependencies {
24+
api "com.squareup.anvil:compiler-api:${latest_version}"
25+
26+
// Optional:
27+
compileOnly "com.google.auto.service:auto-service-annotations:1.0"
28+
kapt "com.google.auto.service:auto-service:1.0"
29+
}
30+
```
31+
32+
After that implement the `CodeGenerator` interface:
33+
```kotlin
34+
@AutoService(CodeGenerator::class)
35+
class SampleCodeGenerator : CodeGenerator {
36+
override fun isApplicable(context: AnvilContext): Boolean = true
37+
38+
override fun generateCode(
39+
codeGenDir: File,
40+
module: ModuleDescriptor,
41+
projectFiles: Collection<KtFile>
42+
): Collection<GeneratedFile> {
43+
return projectFiles
44+
.classesAndInnerClass(module)
45+
.filter { it.hasAnnotation(FqName("sample.MyAnnotation"), module) }
46+
.map { clazz ->
47+
// ...
48+
createGeneratedFile(
49+
codeGenDir = codeGenDir,
50+
packageName = // ...
51+
fileName = // ...
52+
content = // ...
53+
)
54+
}
55+
.toList()
56+
}
57+
}
58+
```
59+
60+
Note that the sample code generator is annotated with `@AutoService`. It's recommended to use the
61+
[AutoService](https://github.com/google/auto/tree/master/service) library to generate necessary
62+
code for `ServiceLoader` API. Anvil uses this mechanism to load your custom code generator.
63+
64+
You can generate as many classes and files as you wish. You can even generate code that uses Anvil
65+
or Dagger annotations and Anvil will process these files. The `generateCode()` function is called
66+
multiple times until no code generators generate code anymore.
67+
68+
To use your new code generator in any module you need to add the module to the Anvil classpath:
69+
```groovy
70+
plugins {
71+
id 'com.squareup.anvil' version "${latest_version}"
72+
}
73+
74+
dependencies {
75+
anvil project(':sample:code-generator')
76+
implementation project(':sample:annotation')
77+
}
78+
```
79+
80+
## Limitations
81+
Anvil code generators are no replacement for Java annotation processing or Kotlin symbol processing.
82+
If you want to generate code independent of Anvil, then it's better to rely on an annotation
83+
processor, KSP or your own Kotlin compiler plugin instead.
84+
85+
Anvil code generators can only generate new code and not modify or remove existing code. For that
86+
you need to create your own compiler plugin.

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
GROUP=com.squareup.anvil
2-
VERSION_NAME=2.2.4-SNAPSHOT
2+
VERSION_NAME=2.3.0-SNAPSHOT
33

44
POM_DESCRIPTION=A Kotlin compiler plugin to make dependency injection with Dagger 2 easier by automatically merging Dagger modules and component interfaces.
55
POM_INCEPTION_YEAR=2020

0 commit comments

Comments
 (0)