diff --git a/docs/topics/ksp/ksp-quickstart.md b/docs/topics/ksp/ksp-quickstart.md
index 641c38b3396..bbbfb57456c 100644
--- a/docs/topics/ksp/ksp-quickstart.md
+++ b/docs/topics/ksp/ksp-quickstart.md
@@ -1,81 +1,58 @@
[//]: # (title: KSP quickstart)
-For a quick start, you can create your own processor or get a [sample one](https://github.com/google/ksp/tree/main/examples/playground).
+In this guide you will learn:
-## Add a processor
+* How to add KSP-based annotation processors to your project.
+* How to create your own annotation processor with the KSP API.
+* Where to find the code generated by the processor.
-To add a processor, you need to include the KSP Gradle Plugin and add a dependency on the processor:
+## Add a KSP-based processor to your project
-1. Add the KSP Gradle Plugin `com.google.devtools.ksp` to your `build.gradle(.kts)` file:
+To use an external processor in your project, add KSP to the [`plugins{}` block](https://docs.gradle.org/current/userguide/plugins.html#sec:plugins_block) in your `build.gradle(.kts)` file. If the processor is
+only needed in a specific module, add it to that module's `build.gradle(.kts)` file instead.
-
-
-
- ```kotlin
- plugins {
- id("com.google.devtools.ksp") version "%kspVersion%"
- }
- ```
-
-
-
-
- ```groovy
- plugins {
- id 'com.google.devtools.ksp' version '%kspVersion%'
- }
- ```
-
-
-
-
-2. Add a dependency on the processor.
-This example uses [Dagger](https://dagger.dev/dev-guide/ksp.html). Replace it with the processor you want to add.
-
-
-
-
- ```kotlin
- dependencies {
- implementation("com.google.dagger:dagger-compiler:2.51.1")
- ksp("com.google.dagger:dagger-compiler:2.51.1")
- }
- ```
-
-
-
-
- ```groovy
- dependencies {
- implementation 'com.google.dagger:dagger-compiler:2.51.1'
- ksp 'com.google.dagger:dagger-compiler:2.51.1'
- }
- ```
-
-
-
+
+
-3. Run `./gradlew build`. You can find the generated code in the `build/generated/ksp` directory.
+```kotlin
+// build.gradle.kts
-Here is a full example:
+plugins {
+ kotlin("jvm") version "2.3.0"
+ id("com.google.devtools.ksp") version "2.3.6"
+}
+```
-
-
+
+
+
+```groovy
+// build.gradle
-```kotlin
plugins {
- id("com.google.devtools.ksp") version "%kspVersion%"
- kotlin("jvm")
+ id 'org.jetbrains.kotlin.jvm' version '2.3.0'
+ id 'com.google.devtools.ksp' version '2.3.6'
}
+```
-repositories {
- mavenCentral()
-}
+
+
+
+> To find the latest version of KSP, check the GitHub [Releases](https://github.com/google/ksp/releases).
+>
+{style="tip"}
+
+In the top-level `dependencies{}` block, add the processor you want to use. In this example, we use
+[Moshi](https://github.com/square/moshi?tab=readme-ov-file#codegen), but the approach is the same for other processors:
+
+
+
+
+```kotlin
+// build.gradle.kts
dependencies {
- implementation(kotlin("stdlib-jdk8"))
- implementation("com.google.dagger:dagger-compiler:2.51.1")
- ksp("com.google.dagger:dagger-compiler:2.51.1")
+ ksp("com.squareup.moshi:moshi-kotlin-codegen:1.15.2")
}
```
@@ -83,210 +60,88 @@ dependencies {
```groovy
-plugins {
- id 'com.google.devtools.ksp' version '%kspSupportedKotlinVersion%-%%'
- id 'org.jetbrains.kotlin.jvm' version '%kotlinVersion%'
+// build.gradle
+
+dependencies {
+ ksp 'com.squareup.moshi:moshi-kotlin-codegen:1.15.2'
}
+```
+
+
+
+
+
+## Create your own processor
+
+By following these steps you will create a simple annotation processor that will generate a `helloWorld()` function.
+While not very useful in practice, it demonstrates the basics of creating your own processors and annotations.
+
+An outline of the final [project structure](#project-structure) is available below for reference.
-repositories {
- mavenCentral()
+### Add KSP to the project
+
+Start from an empty project, and add KSP as a plugin in the top-level `build.gradle(.kts)` file:
+
+
+
+
+```kotlin
+// build.gradle.kts
+
+plugins {
+ kotlin("jvm") version "2.3.0"
+ id("com.google.devtools.ksp") version "2.3.6" apply false
}
+```
-dependencies {
- implementation 'org.jetbrains.kotlin:kotlin-stdlib:%kotlinVersion%'
- implementation 'com.google.dagger:dagger-compiler:2.51.1'
- ksp 'com.google.dagger:dagger-compiler:2.51.1'
+
+
+
+```groovy
+// build.gradle
+
+plugins {
+ id 'org.jetbrains.kotlin.jvm' version '2.3.0'
+ id 'com.google.devtools.ksp' version '2.3.6' apply false
}
```
-## Create a processor of your own
+### Create an annotation
-1. Create an empty gradle project.
-2. Specify version `%kspSupportedKotlinVersion%` of the Kotlin plugin in the root project for use in other project modules:
+Create a new Kotlin module in the project from **File** | **New** | **Module** and name it `annotations`.
-
-
-
- ```kotlin
- plugins {
- kotlin("jvm") version "%kspSupportedKotlinVersion%" apply false
- }
-
- buildscript {
- dependencies {
- classpath(kotlin("gradle-plugin", version = "%kspSupportedKotlinVersion%"))
- }
- }
- ```
-
-
-
-
- ```groovy
- plugins {
- id 'org.jetbrains.kotlin.jvm' version '%kspSupportedKotlinVersion%' apply false
- }
-
- buildscript {
- dependencies {
- classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:%kspSupportedKotlinVersion%'
- }
- }
- ```
-
-
-
+Next, create the following file and declare the annotation `HelloWorldAnnotation`:
-3. Add a module for hosting the processor.
+```kotlin
+//annotations/src/main/kotlin/com/example/annotations/MyAnnotation.kt
+package com.example.annotations
-4. In the module's build script, apply Kotlin plugin and add the KSP API to the `dependencies` block.
+annotation class HelloWorldAnnotation
+```
-
-
-
- ```kotlin
- plugins {
- kotlin("jvm")
- }
-
- repositories {
- mavenCentral()
- }
-
- dependencies {
- implementation("com.google.devtools.ksp:symbol-processing-api:%kspVersion%")
- }
- ```
-
-
-
-
- ```groovy
- plugins {
- id 'org.jetbrains.kotlin.jvm' version '%kotlinVersion%'
- }
-
- repositories {
- mavenCentral()
- }
-
- dependencies {
- implementation 'com.google.devtools.ksp:symbol-processing-api:%kspVersion%'
- }
- ```
-
-
-
-
-5. You'll need to implement [`com.google.devtools.ksp.processing.SymbolProcessor`](https://github.com/google/ksp/tree/main/api/src/main/kotlin/com/google/devtools/ksp/processing/SymbolProcessor.kt)
- and [`com.google.devtools.ksp.processing.SymbolProcessorProvider`](https://github.com/google/ksp/tree/main/api/src/main/kotlin/com/google/devtools/ksp/processing/SymbolProcessorProvider.kt).
- Your implementation of `SymbolProcessorProvider` will be loaded as a service to instantiate the `SymbolProcessor` you implement.
- Note the following:
- * Implement [`SymbolProcessorProvider.create()`](https://github.com/google/ksp/blob/master/api/src/main/kotlin/com/google/devtools/ksp/processing/SymbolProcessorProvider.kt)
- to create a `SymbolProcessor`. Pass dependencies that your processor needs (such as `CodeGenerator`, processor options)
- through the parameters of `SymbolProcessorProvider.create()`.
- * Your main logic should be in the [`SymbolProcessor.process()`](https://github.com/google/ksp/blob/master/api/src/main/kotlin/com/google/devtools/ksp/processing/SymbolProcessor.kt) method.
- * Use `resolver.getSymbolsWithAnnotation()` to get the symbols you want to process, given the fully-qualified name of
- an annotation.
- * A common use case for KSP is to implement a customized visitor (interface `com.google.devtools.ksp.symbol.KSVisitor`)
- for operating on symbols. A simple template visitor is `com.google.devtools.ksp.symbol.KSDefaultVisitor`.
- * For sample implementations of the `SymbolProcessorProvider` and `SymbolProcessor` interfaces, see the following files
- in the sample project.
- * `src/main/kotlin/BuilderProcessor.kt`
- * `src/main/kotlin/TestProcessor.kt`
- * After writing your own processor, register your processor provider to the package by including its fully-qualified
- name in `src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider`.
-
-## Use your own processor in a project
-
-1. Create another module that contains a workload where you want to try out your processor.
-
-
-
-
- ```kotlin
- pluginManagement {
- repositories {
- gradlePluginPortal()
- }
- }
- ```
-
-
-
-
- ```groovy
- pluginManagement {
- repositories {
- gradlePluginPortal()
- }
- }
- ```
-
-
-
-
-2. In the module's build script, apply the `com.google.devtools.ksp` plugin with the specified version and
- add your processor to the list of dependencies.
-
-
-
-
- ```kotlin
- plugins {
- id("com.google.devtools.ksp") version "%kspVersion%"
- }
-
- dependencies {
- implementation(kotlin("stdlib-jdk8"))
- implementation(project(":test-processor"))
- ksp(project(":test-processor"))
- }
- ```
-
-
-
-
- ```groovy
- plugins {
- id 'com.google.devtools.ksp' version '%kspVersion%'
- }
-
- dependencies {
- implementation 'org.jetbrains.kotlin:kotlin-stdlib:%kotlinVersion%'
- implementation project(':test-processor')
- ksp project(':test-processor')
- }
- ```
-
-
-
+### Create and register a processor
-3. Run `./gradlew build`. You can find the generated code under
- `build/generated/ksp`.
+Create another Kotlin module. You can give it whatever name you want. In this guide we name it `processor`.
-Here's a sample build script to apply the KSP plugin to a workload:
+Add the necessary dependencies to the module's `build.gradle(.kts)`. You will need the KSP API and the annotation
+you just declared:
-
+
```kotlin
-plugins {
- id("com.google.devtools.ksp") version "%kspVersion%"
- kotlin("jvm")
-}
-
-repositories {
- mavenCentral()
+// processor/build.gradle.kts
+
+plugins {
+ kotlin("jvm")
}
-
+
dependencies {
- implementation(kotlin("stdlib-jdk8"))
- implementation(project(":test-processor"))
- ksp(project(":test-processor"))
+ implementation(project(":annotations"))
+ implementation("com.google.devtools.ksp:symbol-processing-api:2.3.6")
}
```
@@ -294,73 +149,141 @@ dependencies {
```groovy
-plugins {
- id 'com.google.devtools.ksp' version '%kspVersion%'
- id 'org.jetbrains.kotlin.jvm' version '%kotlinVersion%'
-}
+// processor/build.gradle
-repositories {
- mavenCentral()
+plugins {
+ id 'org.jetbrains.kotlin.jvm'
}
dependencies {
- implementation 'org.jetbrains.kotlin:kotlin-stdlib:%kotlinVersion%'
- implementation project(':test-processor')
- ksp project(':test-processor')
+ implementation project ':annotations'
+ implementation 'com.google.devtools.ksp:symbol-processing-api:2.3.6'
}
```
-## Pass options to processors
+Copy the following code in a new file called `HelloWorldProcessor.kt`:
-Processor options in `SymbolProcessorEnvironment.options` are specified in gradle build scripts:
+```kotlin
+// processor/src/main/kotlin/HelloWorldProcessor.kt
+class HelloWorldProcessor(val codeGenerator: CodeGenerator) : SymbolProcessor {
+ // (1) process() method
+ override fun process(resolver: Resolver): List {
+ resolver
+ .getSymbolsWithAnnotation("com.example.annotations.HelloWorldAnnotation")
+ .filter { it.validate() }
+ .filterIsInstance()
+ .forEach { it.accept(HelloWorldVisitor(), Unit) }
+
+ return emptyList()
+ }
+
+ // (2) visitor
+ inner class HelloWorldVisitor : KSVisitorVoid() {
+ override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {
+ createNewFileFrom(function).use { file ->
+ file.write(
+ """
+ fun helloWorld(): Unit {
+ println("Hello world from function generated by KSP")
+ }
+ """.trimIndent()
+ )
+ }
+ }
+ }
+
+ // (3a)
+ private fun createNewFileFrom(function: KSFunctionDeclaration): OutputStream {
+ return codeGenerator.createNewFile(
+ dependencies = createDependencyOn(function),
+ packageName = "",
+ fileName = "GeneratedHelloWorld"
+ )
+ }
+
+ // (3b)
+ private fun createDependencyOn(function: KSFunctionDeclaration): Dependencies {
+ return Dependencies(aggregating = false, function.containingFile!!)
+ }
+}
-```none
-ksp {
- arg("option1", "value1")
- arg("option2", "value2")
- ...
+// Util function
+fun OutputStream.write(string: String): Unit {
+ this.write(string.toByteArray())
}
```
-## Make IDE aware of generated code
+Let's go through it:
-> Generated source files are registered automatically since KSP 1.8.0-1.0.9.
-> If you're using KSP 1.0.9 or newer and don't need to make the IDE aware of generated resources,
-> feel free to skip this section.
->
-{style="note"}
+1. The main logic of the processor is in the `process()` method. In this case, the method gets a list of every symbol
+ annotated with `HelloWorldAnnotation`, and calls `HelloWorldVisitor` for each of them.
-By default, IntelliJ IDEA or other IDEs don't know about the generated code. So it will mark references to generated
-symbols unresolvable. To make an IDE be able to reason about the generated symbols, mark the
-following paths as generated source roots:
+ The method `process()` returns a list of unprocessed symbols, to be processed at a later round. In our case, we can
+ safely return an emptyList(). For more information, see [Multiple round processing](ksp-multi-round.md).
-```text
-build/generated/ksp/main/kotlin/
-build/generated/ksp/main/java/
+
+2. Processors traverse KSP's view of the Kotlin abstract syntax tree (AST) using visitors. Inside the
+ `HelloWorldPocessor` class, create a visitor class. `HelloWorldAnnotation` will only be used on a function, so we only need
+ to override the `visitFunctionDeclaration()` method.
+
+ > `KSVisitorVoid` is one of the visitors KSP provides to be overridden and adapted. It is also possible to create
+ > your own by implementing the `KSVisitor` [interface](https://github.com/google/ksp/blob/main/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSVisitor.kt).
+ >
+ {style="tip"}
+
+3. `createNewFile()` and `createDependency()` are used to create the file where KSP will put the generated code.
+
+Next, you need to implement a `SymbolProcessorProvider`. The KSP framework calls this class to construct the processor.
+Create a file and copy the following code:
+
+```kotlin
+// processor/src/main/kotlin/HelloWorldProcessorProvider.kt
+
+class HelloWorldProcessorProvider : SymbolProcessorProvider {
+ override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
+ return HelloWorldProcessor(environment.codeGenerator)
+ }
+}
```
-If your IDE supports resource directories, also mark the following one:
+> Any dependencies your processor needs (such as `CodeGenerator`) will be passed as parameters to the `create()`
+> method.
+>
+{style="tip"}
-```text
-build/generated/ksp/main/resources/
+Finally, register the processor provider. To do that, create the following file, and copy in it the fully qualified
+name of the provider:
+
+```kotlin
+//processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
+
+HelloWorldProcessorProvider
```
-It may also be necessary to configure these directories in your KSP consumer module's build script:
+### Test your processor
+
+Create one last module and name it `app`. In the module's `build.gradle(.kts)`:
-
+* Add KSP to the `plugins {}` block.
+* Add your processor and annotation to the `dependencies {}` block.
+
+
```kotlin
-kotlin {
- sourceSets.main {
- kotlin.srcDir("build/generated/ksp/main/kotlin")
- }
- sourceSets.test {
- kotlin.srcDir("build/generated/ksp/test/kotlin")
- }
+// app/build.gradle.kts
+
+plugins {
+ kotlin("jvm")
+ id("com.google.devtools.ksp")
+}
+
+dependencies {
+ implementation(project(":annotations"))
+ ksp(project(":processor"))
}
```
@@ -368,63 +291,110 @@ kotlin {
```groovy
-kotlin {
- sourceSets {
- main.kotlin.srcDirs += 'build/generated/ksp/main/kotlin'
- test.kotlin.srcDirs += 'build/generated/ksp/test/kotlin'
- }
+// app/build.gradle
+
+plugins {
+ id 'com.google.devtools.ksp'
+}
+
+dependencies {
+ implementation project (':annotations')
+ ksp project (':processor')
}
```
-If you are using IntelliJ IDEA and KSP in a Gradle plugin then the above snippet will give the following warning:
-```text
-Execution optimizations have been disabled for task ':publishPluginJar' to ensure correctness due to the following reasons:
-Gradle detected a problem with the following location: '../build/generated/ksp/main/kotlin'.
-Reason: Task ':publishPluginJar' uses this output of task ':kspKotlin' without declaring an explicit or implicit dependency.
-```
-
-In this case, use the following script instead:
-
-
+In the top-level `settings.gradle.kts`, include all the submodules:
+
+
```kotlin
-plugins {
- // ...
- idea
-}
+// settings.gradle.kts
-idea {
- module {
- // Not using += due to https://github.com/gradle/gradle/issues/8749
- sourceDirs = sourceDirs + file("build/generated/ksp/main/kotlin") // or tasks["kspKotlin"].destination
- testSourceDirs = testSourceDirs + file("build/generated/ksp/test/kotlin")
- generatedSourceDirs = generatedSourceDirs + file("build/generated/ksp/main/kotlin") + file("build/generated/ksp/test/kotlin")
- }
-}
+include("annotations")
+include("app")
+include("processor")
```
```groovy
-plugins {
- // ...
- id 'idea'
-}
+// settings.gradle
-idea {
- module {
- // Not using += due to https://github.com/gradle/gradle/issues/8749
- sourceDirs = sourceDirs + file('build/generated/ksp/main/kotlin') // or tasks["kspKotlin"].destination
- testSourceDirs = testSourceDirs + file('build/generated/ksp/test/kotlin')
- generatedSourceDirs = generatedSourceDirs + file('build/generated/ksp/main/kotlin') + file('build/generated/ksp/test/kotlin')
- }
-}
+include 'processor'
+include 'annotations'
+include 'app'
```
+
+Now you are ready to try it. In the `app` module, create `Main.kt` and add the following code:
+
+```kotlin
+// app/src/main/kotlin/Main.kt
+import com.example.annotations.HelloWorldAnnotation
+
+@HelloWorldAnnotation
+fun main() {
+ helloWorld()
+}
+```
+
+Notice that, in `main()`, the function `helloWorld()` is called. It doesn’t exist yet, and your IDE might flag this.
+But KSP will create it when you compile and run your project, so it won’t be a problem!
+
+Run the program. You should see the output of the `helloWorld()` function in your console.
+
+The code generated by KSP can be found here:
+
+```text
+app/build/generated/ksp/main/kotlin/GeneratedHelloWorld.kt
+```
+
+### Project structure
+
+Your project’s final file structure should look like this (you might have additional directories, for example
+`gradle`, `gradlew`, or `.idea`):
+
+```text
+.
+├── app
+│ ├── build.gradle.kts
+│ └── src
+│ └── main
+│ └── kotlin
+│ └── Main.kt
+├── annotations
+│ ├── build.gradle.kts
+│ └── src
+│ └── main
+│ └── kotlin
+| └── com
+| └── example
+| └── annotations
+| └── HelloWorldAnnotation.kt
+├── processor
+│ ├── build.gradle.kts
+│ └── src
+│ └── main
+│ ├── kotlin
+│ │ ├── HelloWorldProcessor.kt
+│ │ └── HelloWorldProcessorProvider.kt
+│ └── resources/META-INF/services
+| └── com.google.devtools.ksp.processing.SymbolProcessorProvider
+├── build.gradle.kts
+└── settings.gradle.kts
+
+```
+
+
+## What's next?
+
+* Explore the full code for this example in [the KSP repository](https://github.com/google/ksp/pull/2804/changes).
+* Find more complex, real-world examples in [the KSP repository](https://github.com/google/ksp/blob/main/examples/playground/test-processor/src/main/kotlin/BuilderProcessor.kt).
+* Browse the list of [supported libraries](ksp-overview.md#supported-libraries).
\ No newline at end of file