-
Notifications
You must be signed in to change notification settings - Fork 4.5k
update: ksp-multiplatform.md #5584
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
73396ad
f52b5df
59c5f34
d7da5ca
5eec5fc
ad5727e
9531cae
23c70c4
6e6de18
6f5be47
b5b0edc
bfc2cdb
ac6da33
9c15f72
d2b0548
4dad27e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,56 +1,198 @@ | ||
| [//]: # (title: KSP with Kotlin Multiplatform) | ||
| [//]: # (description: Add KSP to a Kotlin multiplatform project) | ||
|
|
||
| For a quick start, see a [sample Kotlin Multiplatform project](https://github.com/google/ksp/tree/main/examples/multiplatform) | ||
| defining a KSP processor. | ||
| Here you can learn how to use Kotlin Symbol Processing (KSP) in a Kotlin Multiplatform project. For a quick start, see | ||
| an example of a multiplatform project with several targets using KSP in the | ||
| [source repository](https://github.com/google/ksp/tree/main/examples/multiplatform). The processor in this example | ||
| generates a `Foo` class used by the project. | ||
|
|
||
| Starting from KSP 1.0.1, applying KSP on a multiplatform project is similar to that on a single platform, JVM project. | ||
| The main difference is that, instead of writing the `ksp(...)` configuration in dependencies, `add(ksp<Target>)` or `add(ksp<SourceSet>)` | ||
| is used to specify which compilation targets need symbol processing, before compilation. | ||
| ## Add KSP to a multiplatform project | ||
|
|
||
| In the client module's `build.gradle.kts` file, assign a processor to each target that needs symbol processing in the | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is a client module? 🤔
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure that we use this term anywhere, maybe I'm wrong?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we use "assign" as a word here? Isn't it just adding a dependency? |
||
| `dependencies {}` block: | ||
|
|
||
| ``` | ||
| add("ksp<Target>", <processor>) | ||
| ``` | ||
|
|
||
| `<Target>` is one of the targets used in your multiplatform project. For a full list of targets, see | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For a full list of target — could be a tip, no?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can place both Target and Processor in a list, so the info about them would be structured. |
||
| [Multiplatform Gradle DSL reference](https://kotlinlang.org/docs/multiplatform/multiplatform-dsl-reference.html#targets) | ||
| and [Kotlin/Native supported targets](https://kotlinlang.org/docs/native-target-support.html). | ||
|
|
||
| `<processor>` is a Gradle project path. It can be: | ||
|
|
||
| * a specific directory in your project that contains the logic for your symbol processor: | ||
|
|
||
| ``` | ||
| add("kspJvm", project(":local-processor")) | ||
| ``` | ||
|
|
||
| * an external processor such as Room: | ||
|
|
||
| ``` | ||
| add("kspJvm", "androidx.room:room-compiler:2.6.1") | ||
| ``` | ||
|
|
||
| A single target can have multiple processors: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't you want to put these different build set ups into separte subsections? |
||
|
|
||
| <tabs group="build-script"> | ||
| <tab title="Kotlin" group-key="kotlin"> | ||
|
|
||
| ```kotlin | ||
| plugins { | ||
| kotlin("multiplatform") | ||
| id("com.google.devtools.ksp") | ||
| } | ||
| add("kspAndroid", project(":test-processor")) | ||
| add("kspAndroid", "androidx.room:room-compiler:2.6.1") | ||
| ``` | ||
|
|
||
| </tab> | ||
| <tab title="Groovy" group-key="groovy"> | ||
|
|
||
| ```Groovy | ||
| add('kspAndroid', project(':test-processor')) | ||
| add('kspAndroid', 'androidx.room:room-compiler:2.6.1') | ||
| ``` | ||
|
|
||
| </tab> | ||
| </tabs> | ||
|
|
||
| The same processor can be used in multiple targets: | ||
|
|
||
| <tabs group="build-script"> | ||
| <tab title="Kotlin" group-key="kotlin"> | ||
|
|
||
| ```kotlin | ||
| add("kspIosX64", project(":test-processor")) | ||
| add("kspIosArm64", project(":test-processor")) | ||
| add("kspIosSimulatorArm64", project(":test-processor")) | ||
| ``` | ||
|
|
||
| </tab> | ||
| <tab title="Groovy" group-key="groovy"> | ||
|
|
||
| ```Groovy | ||
| add('kspIosX64', project(':test-processor')) | ||
| add('kspIosArm64', project(':test-processor')) | ||
| add('kspIosSimulatorArm64', project(':test-processor')) | ||
| ``` | ||
|
|
||
| </tab> | ||
| </tabs> | ||
|
|
||
| If you have many iOS targets, you can avoid repetition by looping: | ||
|
|
||
| <tabs group="build-script"> | ||
| <tab title="Kotlin" group-key="kotlin"> | ||
|
|
||
| kotlin { | ||
| jvm() | ||
| linuxX64 { | ||
| binaries { | ||
| executable() | ||
| } | ||
| } | ||
| ```kotlin | ||
| kotlin.targets.filter { it.name.startsWith("ios") }.forEach { target -> | ||
| add( | ||
| "ksp${target.name.replaceFirstChar { it.uppercaseChar() }}", | ||
| project(":test-processor") | ||
| ) | ||
| } | ||
| ``` | ||
|
|
||
| dependencies { | ||
| add("kspCommonMainMetadata", project(":test-processor")) | ||
| add("kspJvm", project(":test-processor")) | ||
| add("kspJvmTest", project(":test-processor")) // Not doing anything because there's no test source set for JVM | ||
| // There is no processing for the Linux x64 main source set, because kspLinuxX64 isn't specified | ||
| // add("kspLinuxX64Test", project(":test-processor")) | ||
| </tab> | ||
| <tab title="Groovy" group-key="groovy"> | ||
|
|
||
| ```Groovy | ||
| kotlin.targets.filter { it.name.startsWith("ios") }.forEach { target -> | ||
| add( | ||
| "ksp${target.name.replaceFirstChar { it.uppercaseChar() }}", | ||
| project(":test-processor") | ||
| ) | ||
| } | ||
| ``` | ||
|
|
||
| </tab> | ||
| </tabs> | ||
|
|
||
| You can also configure KSP for test compilations: | ||
|
|
||
| <tabs group="build-script"> | ||
| <tab title="Kotlin" group-key="kotlin"> | ||
|
|
||
| ```kotlin | ||
| add("kspJvmTest", project(":test-processor")) | ||
| add("kspJsTest", project(":test-processor")) | ||
| add("kspIosX64Test", project(":test-processor")) | ||
| ``` | ||
|
|
||
| </tab> | ||
| <tab title="Groovy" group-key="groovy"> | ||
|
|
||
| ```Groovy | ||
| add('kspJvmTest', project(':test-processor')) | ||
| add('kspJsTest', project(':test-processor')) | ||
| add('kspIosX64Test', project(':test-processor')) | ||
| ``` | ||
|
|
||
| </tab> | ||
| </tabs> | ||
|
|
||
| For Android host and device tests, KSP derives configuration names from the corresponding source set names: | ||
|
|
||
| <tabs group="build-script"> | ||
| <tab title="Kotlin" group-key="kotlin"> | ||
|
|
||
| ```kotlin | ||
| add("kspAndroidHostTest", project(":test-processor")) | ||
| add("kspAndroidDeviceTest", project(":test-processor")) | ||
| ``` | ||
|
|
||
| </tab> | ||
| <tab title="Groovy" group-key="groovy"> | ||
|
|
||
| ```Groovy | ||
| add('kspAndroidHostTest', project(':test-processor')) | ||
| add('kspAndroidDeviceTest', project(':test-processor')) | ||
| ``` | ||
|
|
||
| </tab> | ||
| </tabs> | ||
|
|
||
| ## Find KSP configuration names | ||
|
|
||
| KSP configuration names depend on the source set names generated by the Kotlin plugin. To view the complete | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the Kotlin plugin? Is it KGP (Kotlin Gradle Plugin)? Or Kotlin IntelliJ Plugin? Or Kotlin Multiplatfrom Plugin? |
||
| list of KSP configurations for a module, run: | ||
|
|
||
| ```Bash | ||
| ./gradlew :<your-module-name>:dependencies | grep ksp | ||
| ``` | ||
|
|
||
| Look for the configuration names that correspond to your target source sets. | ||
|
|
||
| > Since KSP 2, the catch-all `ksp(...)` configuration is deprecated. Configure each target explicitly to avoid running | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WDYT about making this one a warning instead of note? Will it work? |
||
| > processors where they are not needed. | ||
| > | ||
| {style="note"} | ||
|
|
||
| ## Compilation and processing | ||
|
|
||
| In a multiplatform project, Kotlin compilation may happen multiple times (`main`, `test`, or other build flavors) for each platform. | ||
| So is symbol processing. A symbol processing task is created whenever there is a Kotlin compilation task and a | ||
| corresponding `ksp<Target>` or `ksp<SourceSet>` configuration is specified. | ||
| In a multiplatform project, Kotlin creates a separate [compilation](https://kotlinlang.org/docs/multiplatform/multiplatform-advanced-project-structure.html#compilations) | ||
| for each target and source set, such as `main` and`test`. For every eligible Kotlin compilation task, KSP creates a | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is eligible Kotlin compilation task?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we just omit it? |
||
| corresponding symbol processing task. | ||
|
|
||
| The [example project](https://github.com/google/ksp/tree/main/examples/multiplatform) defines six targets: | ||
|
|
||
| For example, in the above `build.gradle.kts`, there are 4 compilation tasks: common/metadata, JVM main, Linux x64 main, Linux x64 test, | ||
| and 3 symbol processing tasks: common/metadata, JVM main, Linux x64 test. | ||
| * JVM | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think to move the info about main and test before this list, maybe rephrase, and in the list mention it in the way:
|
||
| * JS | ||
| * Linux X64 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we write the target names as one word? |
||
| * Android Native X64 | ||
| * Android Native Arm64 | ||
| * Mingw X64 | ||
|
budindepunk marked this conversation as resolved.
|
||
|
|
||
| ## Avoid the ksp(...) configuration on KSP 1.0.1+ | ||
| Each of these targets includes a `main` and a `test` compilation. As a result, the project creates at least 12 Kotlin | ||
| compilation tasks. | ||
|
|
||
| Before KSP 1.0.1, there is only one, unified `ksp(...)` configuration available. Therefore, processors either applies to all | ||
| compilation targets, or nothing at all. Note that the `ksp(...)` configuration not only applies to the main source set, but also | ||
| the test source set if it exists, even on traditional, non-multiplatform projects. This brought unnecessary overheads to build time. | ||
| In the example's `workload/build.gradle.kts` file, KSP dependencies are declared for the following configurations: | ||
|
|
||
| Starting from KSP 1.0.1, per-target configurations are provided as shown in the above example. In the future: | ||
| 1. For multiplatform projects, the `ksp(...)` configuration will be deprecated and removed. | ||
| 2. For single platform projects, the `ksp(...)` configuration will only apply to the main, default compilation. | ||
| Other targets like `test` will need to specify `kspTest(...)` in order to apply processors. | ||
| * `kspJvm` and `kspJvmTest` | ||
| * `kspJs` and `kspJsTest` | ||
| * `kspAndroidNativeX64` and `kspAndroidNativeX64Test` | ||
| * `kspAndroidNativeArm64` and `kspAndroidNativeArm64Test` | ||
| * `kspLinuxX64` | ||
| * `kspMingwX64` | ||
|
|
||
| Starting from KSP 1.0.1, there is an early access flag `-DallowAllTargetConfiguration=false` to switch to the more efficient behavior. | ||
| If the current behavior is causing performance issues, please give it a try. | ||
| The default value of the flag will be flipped from `true` to `false` on KSP 2.0. | ||
| KSP creates a symbol processing task for each configuration where a KSP dependency is declared. In this example, the | ||
| project creates at least 12 Kotlin compilation tasks and 10 symbol processing tasks. The remaining compilations don't | ||
| have corresponding KSP tasks because KSP isn't configured for them. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.