From a13f19b2b3790c4206b78c69772956f6536ec780 Mon Sep 17 00:00:00 2001 From: Sarah Haggarty Date: Wed, 28 Aug 2024 11:47:35 +0200 Subject: [PATCH 01/32] update: add @SubclassOptInRequired documentation --- docs/topics/opt-in-requirements.md | 329 +++++++++++++++++++++-------- 1 file changed, 244 insertions(+), 85 deletions(-) diff --git a/docs/topics/opt-in-requirements.md b/docs/topics/opt-in-requirements.md index 729aa7c9bab..d11e6320381 100644 --- a/docs/topics/opt-in-requirements.md +++ b/docs/topics/opt-in-requirements.md @@ -1,123 +1,172 @@ [//]: # (title: Opt-in requirements) -The Kotlin standard library provides a mechanism for requiring and giving explicit consent for using certain elements of APIs. -This mechanism lets library developers inform users of their APIs about specific conditions that require opt-in, -for example, if an API is in the experimental state and is likely to change in the future. +The Kotlin standard library provides a mechanism for requiring and giving explicit consent to use certain API elements. +This mechanism allows library authors to inform users about specific conditions that require opt-in, +such as when an API is in an experimental state and is likely to change in the future. -To prevent potential issues, the compiler warns users of such APIs about these conditions and -requires them to opt in before using the API. +To protect users, the compiler warns about these conditions and requires them to opt in before the API can be used. -## Opt in to using API +## Opt in to API -If a library author marks a declaration from a library's API as _[requiring opt-in](#require-opt-in-for-api)_, -you should give an explicit consent for using it in your code. -There are several ways to opt in to such APIs, all applicable without technical limitations. -You are free to choose the way that you find best for your situation. +If a library author marks a declaration from their library's API as **[requiring opt-in](#require-opt-in-to-use-api)**, +you must give explicit consent before you can use it in your code. +There are several ways to opt in. We recommend choosing the approach that best suits your situation. -### Propagating opt-in +### Opt in locally -When you use an API in the code intended for third-party use (a library), you can propagate its opt-in requirement to your API as well. -To do this, annotate your declaration with the _[opt-in requirement annotation](#create-opt-in-requirement-annotations)_ of the API used in its body. -This enables you to use API elements that require opt-in. +To opt in to a specific API element when you use it in your code, use the [`@OptIn`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-opt-in/) +annotation with a reference to the experimental API marker. For example, suppose you want to use the `DateProvider` class, +which requires an opt-in: ```kotlin // Library code -@RequiresOptIn(message = "This API is experimental. It may be changed in the future without notice.") +@RequiresOptIn(message = "This API is experimental. It could change in the future without notice.") @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) -annotation class MyDateTime // Opt-in requirement annotation +annotation class MyDateTime -@MyDateTime -class DateProvider // A class requiring opt-in +@MyDateTime +// A class requiring opt-in +class DateProvider ``` +In your code, before declaring a function that uses the `DateProvider` class, add the `@OptIn` annotation with +a reference to the `MyDateTime` annotation class: + ```kotlin // Client code -fun getYear(): Int { - val dateProvider: DateProvider // Error: DateProvider requires opt-in +@OptIn(MyDateTime::class) + +// Uses DateProvider +fun getDate(): Date { + val dateProvider: DateProvider // ... } +``` -@MyDateTime -fun getDate(): Date { - val dateProvider: DateProvider // OK: the function requires opt-in as well +It's important to note that with this approach, if the `getDate()` function is called elsewhere in your code or used by +another developer, no opt-in is required: + +```kotlin +// Client code +@OptIn(MyDateTime::class) + +// Uses DateProvider +fun getDate(): Date { + val dateProvider: DateProvider // ... } fun displayDate() { - println(getDate()) // Error: getDate() requires opt-in + // OK: No opt-in is required + println(getDate()) } ``` -As you can see in this example, the annotated function appears to be a part of the `@MyDateTime` API. -So, such an opt-in propagates the opt-in requirement to the client code; its clients will see the same warning message -and be required to consent as well. +The opt-in requirement is not propagated, which means others might unknowingly use experimental APIs. To avoid this, it +is safer to propagate the opt-in requirements. -Implicit usages of APIs that require opt-in also require opt-in. If an API element doesn't have an opt-in requirement -annotation but its signature includes a type declared as requiring opt-in, its usage will still raise a warning. -See the example below. +#### Propagate opt-in requirements + +When you use API in your code that's intended for third-party use, such as in a library, you can propagate its opt-in requirement +to your API as well. To do this, mark your declaration with the same **[opt-in requirement annotation](#create-opt-in-requirement-annotations)** +used by the library. + +For example, before declaring a function that uses the `DateProvider` class, add the `@MyDateTime` annotation: ```kotlin // Client code -fun getDate(dateProvider: DateProvider): Date { // Error: DateProvider requires opt-in +@MyDateTime +fun getDate(): Date { + // OK: the function requires opt-in as well + val dateProvider: DateProvider // ... } fun displayDate() { - println(getDate()) // Warning: the signature of getDate() contains DateProvider, which requires opt-in + println(getDate()) + // Error: getDate() requires opt-in } ``` -To use multiple APIs that require opt-in, mark the declaration with all their opt-in requirement annotations. - -### Non-propagating opt-in +As you can see in this example, the annotated function appears to be a part of the `@MyDateTime` API. +The opt-in propagates the opt-in requirement to users of the `getDate()` function. -In modules that don't expose their own API, such as applications, you can opt in to using APIs without propagating -the opt-in requirement to your code. In this case, mark your declaration with [`@OptIn`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-opt-in/) - passing the opt-in requirement annotation as its argument: +If the signature of an API element includes a type that requires opt-in, the signature itself must also require opt-in. +Otherwise, if an API element doesn't require opt-in, but its signature includes a type that does, using it triggers an error. ```kotlin -// Library code -@RequiresOptIn(message = "This API is experimental. It may be changed in the future without notice.") -@Retention(AnnotationRetention.BINARY) -@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) -annotation class MyDateTime // Opt-in requirement annotation +// Client code +@MyDateTime +fun getDate(dateProvider: DateProvider = DateProvider()): Date -@MyDateTime -class DateProvider // A class requiring opt-in +@MyDateTime +fun displayDate() { + // OK: the function requires opt-in as well + println(getDate()) +} ``` +Similarly, if you apply `@OptIn` to a declaration whose signature includes a type that requires opt-in, the opt-in requirement +still propagates: + ```kotlin // Client code @OptIn(MyDateTime::class) -fun getDate(): Date { // Uses DateProvider; doesn't propagate the opt-in requirement - val dateProvider: DateProvider - // ... -} +// Propagates opt-in due to DateProvider in the signature +fun getDate(dateProvider: DateProvider = DateProvider()): Date fun displayDate() { - println(getDate()) // OK: opt-in is not required + println(getDate()) + // Error: getDate() requires opt-in } ``` -When somebody calls the function `getDate()`, they won't be informed about the opt-in requirements for APIs used in its body. - -Note that if `@OptIn` applies to the declaration whose signature contains a type declared as requiring opt-in, -the opt-in will still propagate: +When propagating opt-in requirements, it's important to understand that if an API element becomes stable and no longer +has an opt-in requirement, any other API elements that still have the opt-in requirement remain experimental. For example, +suppose a library author removes the opt-in requirement for the `getDate()` function because it's now stable: ```kotlin -// Client code -@OptIn(MyDateTime::class) -fun getDate(dateProvider: DateProvider): Date { // Has DateProvider as a part of a signature; propagates the opt-in requirement +// Library code +// No opt-in requirement +fun getDate(): Date { + val dateProvider: DateProvider // ... } +``` + +If you use the `displayDate()` function without removing the opt-in annotation, it remains experimental even though the +opt-in is no longer needed: + +```kotlin +// Client code +// Still experimental! +@MyDateTime fun displayDate() { - println(getDate()) // Warning: getDate() requires opt-in + // Uses a stable library function + println(getDate()) } ``` -To use an API that requires opt-in in all functions and classes in a file, add the file-level annotation `@file:OptIn` +#### Opt in to multiple APIs + +To opt in to multiple APIs, mark the declaration with all their opt-in requirement annotations. For example: + +```kotlin +@ExperimentalCoroutinesApi +@FlowPreview +``` + +Or alternatively with `@OptIn`: + +```kotlin +@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class) +``` + +### Opt in a file + +To use an API that requires opt-in for all functions and classes in a file, add the file-level annotation `@file:OptIn` to the top of the file before the package specification and imports. ```kotlin @@ -125,7 +174,7 @@ to the top of the file before the package specification and imports. @file:OptIn(MyDateTime::class) ``` -### Module-wide opt-in +### Opt in a module > The `-opt-in` compiler option is available since Kotlin 1.6.0. For earlier Kotlin versions, use `-Xopt-in`. > @@ -134,7 +183,7 @@ to the top of the file before the package specification and imports. If you don't want to annotate every usage of APIs that require opt-in, you can opt in to them for your whole module. To opt in to using an API in a module, compile it with the argument `-opt-in`, specifying the fully qualified name of the opt-in requirement annotation of the API you use: `-opt-in=org.mylibrary.OptInAnnotation`. -Compiling with this argument has the same effect as if every declaration in the module had the annotation`@OptIn(OptInAnnotation::class)`. +Compiling with this argument has the same effect as if every declaration in the module has the annotation`@OptIn(OptInAnnotation::class)`. If you build your module with Gradle, you can add arguments like this: @@ -148,7 +197,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask tasks.named>("compileKotlin").configure { compilerOptions.freeCompilerArgs.add("-opt-in=org.mylibrary.OptInAnnotation") } - ``` @@ -197,7 +245,7 @@ sourceSets { -For Maven, it would be: +For Maven, use the following: ```xml @@ -219,12 +267,86 @@ For Maven, it would be: To opt in to multiple APIs on the module level, add one of the described arguments for each opt-in requirement marker used in your module. -## Require opt-in for API +### Opt in to inherit from a class or interface + +Sometimes, a library author provides an API but wants to require users to explicitly opt in before they can extend it. +For example, the library API may be stable for use but not for inheritance, as it might be extended in the future with +new abstract functions. Library authors can enforce this by marking [open](inheritance.md) or [abstract classes](classes.md#abstract-classes) and [non-functional interfaces](interfaces.md) +with the [`@SubclassOptInRequired`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-subclass-opt-in-required/) annotation. + +To opt in to use such an API element and extend it in your code, use the `@SubclassOptInRequired` annotation +with a reference to the annotation class. For example, suppose you want to use the `CoreLibraryApi` interface, which +requires an opt-in: + +```kotlin +// Library code +@RequiresOptIn( + level = RequiresOptIn.Level.WARNING, + message = "Interfaces in this library are experimental" +) +annotation class UnstableApi() + +@SubclassOptInRequired(UnstableApi::class) +// An interface requiring opt-in to extend +interface CoreLibraryApi +``` + +In your code, before creating a new interface that inherits from the `CoreLibraryApi` interface, add the `@SubclassOptInRequired` +annotation with a reference to the `UnstableApi` annotation class: + +```kotlin +// Client code +@SubclassOptInRequired(UnstableApi::class) +interface SomeImplementation : CoreLibraryApi +``` + +Note that when you use the `@SubclassOptInRequired` annotation on a class, the opt-in requirement is not propagated to +any [inner or nested classes](nested-classes.md): + +```kotlin +// Library code +@RequiresOptIn +annotation class ExperimentalFeature + +@SubclassOptInRequired(ExperimentalFeature::class) +open class FileSystem { + open class File +} + +// Client code + +// Opt-in is required +class NetworkFileSystem : FileSystem() + +// Nested class +// No opt-in required +class TextFile : FileSystem.File() +``` + +Alternatively, you can opt in by using the `@OptIn` annotation. You can also use an experimental marker annotation +to propagate the requirement further to any uses of the class in your code: + +```kotlin +// Client code +// With @OptIn annotation +@OptInRequired(UnstableApi::class) +interface SomeImplementation : CoreLibraryApi + +// With annotation referencing annotation class +// Propagates the opt-in requirement further +@UnstableApi +interface SomeImplementation : CoreLibraryApi +``` + +## Require opt-in to use API + +You can require users of your library to opt in before they are able to use your API. Additionally, you can inform users +about any special conditions for using your API until you decide to remove the opt-in requirement. ### Create opt-in requirement annotations -If you want to require explicit consent to using your module's API, create an annotation class to use as an _opt-in requirement annotation_. -This class must be annotated with [@RequiresOptIn](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-requires-opt-in/): +To require opt-in to use your module's API, create an annotation class to use as an **opt-in requirement annotation**. +This class must be annotated with [`@RequiresOptIn`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-requires-opt-in/): ```kotlin @RequiresOptIn @@ -233,19 +355,21 @@ This class must be annotated with [@RequiresOptIn](https://kotlinlang.org/api/la annotation class MyDateTime ``` -Opt-in requirement annotations must meet several requirements: -* `BINARY` or `RUNTIME` [retention](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.annotation/-retention/) -* No `EXPRESSION`, `FILE`, `TYPE`, or `TYPE_PARAMETER` among [targets](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.annotation/-target/) +Opt-in requirement annotations must meet several requirements. They must have: + +* `BINARY` or `RUNTIME` [retention](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.annotation/-retention/). +* `EXPRESSION`, `FILE`, `TYPE`, or `TYPE_PARAMETER` as a [target](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.annotation/-target/). * No parameters. An opt-in requirement can have one of two severity [levels](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-requires-opt-in/-level/): -* `RequiresOptIn.Level.ERROR`. Opt-in is mandatory. Otherwise, the code that uses marked API won't compile. Default level. + +* `RequiresOptIn.Level.ERROR`. Opt-in is mandatory. Otherwise, the code that uses marked API won't compile. This is the default level. * `RequiresOptIn.Level.WARNING`. Opt-in is not mandatory, but advisable. Without it, the compiler raises a warning. To set the desired level, specify the `level` parameter of the `@RequiresOptIn` annotation. -Additionally, you can provide a `message` to inform API users about special condition of using the API. -The compiler will show it to users that use the API without opt-in. +Additionally, you can provide a `message` to API users. The compiler shows this message to users that try to use the API +without opt-in: ```kotlin @RequiresOptIn(level = RequiresOptIn.Level.WARNING, message = "This API is experimental. It can be incompatibly changed in the future.") @@ -255,12 +379,13 @@ annotation class ExperimentalDateTime ``` If you publish multiple independent features that require opt-in, declare an annotation for each. -This makes the use of API safer for your clients: they can use only the features that they explicitly accept. -This also lets you remove the opt-in requirements from the features independently. +This makes using your API safer for your clients because they can use only the features that they explicitly accept. +This also means that you can remove the opt-in requirements from features independently, which makes your API easier +to maintain. ### Mark API elements -To require an opt-in to using an API element, annotate its declaration with an opt-in requirement annotation: +To require an opt-in to use an API element, annotate its declaration with an opt-in requirement annotation: ```kotlin @MyDateTime @@ -271,24 +396,58 @@ fun getTime(): Time {} ``` Note that for some language elements, an opt-in requirement annotation is not applicable: -* You cannot annotate a backing field or a getter of a property, just the property itself. -* You cannot annotate a local variable or a value parameter. + +* You can't annotate a backing field or a getter of a property, just the property itself. +* You can't annotate a local variable or a value parameter. + +## Require opt-in to extend API + +There may be times when you want more granular control over which specific parts of your API can be used and +extended. For example, when you have some API that is stable to use but: + +* **Unstable to implement** due to ongoing evolution, such as when you have a family of interfaces where you expect to add new abstract functions without default implementations. +* **Delicate or fragile to implement**, such as individual functions that need to behave in a coordinated manner. +* **Has a contract that may be weakened in the future** in a backward-incompatible way for external implementations, such as changing an input parameter `T` to a nullable version `T?` where the code didn't previously consider `null` values. + +In such cases, you can require users to opt in to your API before they can extend it further. Users can extend your API +by inheriting from the API or implementing abstract functions. By using the [`@SubclassOptInRequired`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-subclass-opt-in-required/) annotation, +you can enforce this requirement to opt-in for [open](inheritance.md) or [abstract classes](classes.md#abstract-classes) and [non-functional interfaces](interfaces.md). + +To add the opt-in requirement to an API element, use the [`@SubclassOptInRequired`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-subclass-opt-in-required/) +annotation with a reference to the annotation class: + +```kotlin +@RequiresOptIn( + level = RequiresOptIn.Level.WARNING, + message = "Interfaces in this library are experimental" +) +annotation class UnstableApi() + +@SubclassOptInRequired(UnstableApi::class) +// An interface requiring opt-in to extend +interface CoreLibraryApi +``` + +Note that when you use the `@SubclassOptInRequired` annotation to require opt-in, the requirement is not propagated to +any [inner or nested classes](nested-classes.md). + +For a real-world example of how to use the `@SubclassOptInRequired` annotation in your API, check out the [`SharedFlow`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-shared-flow/) +interface in the `kotlinx.coroutines` library. ## Opt-in requirements for pre-stable APIs If you use opt-in requirements for features that are not stable yet, carefully handle the API graduation to avoid -breaking the client code. +breaking client code. -Once your pre-stable API graduates and is released in a stable state, remove its opt-in requirement annotations from declarations. -The clients will be able to use them without restriction. However, you should leave the annotation classes in modules so that -the existing client code remains compatible. +Once your pre-stable API graduates and is released in a stable state, remove opt-in requirement annotations from +your declarations. The clients can then use them without restriction. However, you should leave the annotation classes +in modules so that the existing client code remains compatible. -To let the API users update their modules accordingly (remove the annotations -from their code and recompile), mark the annotations as [`@Deprecated`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-deprecated/) -and provide the explanation in the deprecation message. +To encourage API users to update their modules by removing any annotations from their code and recompiling, mark the annotations +as [`@Deprecated`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-deprecated/) and provide an explanation in the deprecation message. ```kotlin @Deprecated("This opt-in requirement is not used anymore. Remove its usages from your code.") @RequiresOptIn annotation class ExperimentalDateTime -``` \ No newline at end of file +``` From 0fda38811bafaadac0d0d7aa9abff2df8191ad71 Mon Sep 17 00:00:00 2001 From: Sarah Haggarty Date: Fri, 25 Oct 2024 10:04:51 +0200 Subject: [PATCH 02/32] update: remove gradle plugin variants < 7.6.3 --- docs/topics/gradle/gradle-plugin-variants.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/topics/gradle/gradle-plugin-variants.md b/docs/topics/gradle/gradle-plugin-variants.md index b9389237898..fe362f55fc3 100644 --- a/docs/topics/gradle/gradle-plugin-variants.md +++ b/docs/topics/gradle/gradle-plugin-variants.md @@ -15,12 +15,7 @@ Currently, there are the following variants of the Kotlin Gradle plugin: | Variant's name | Corresponding Gradle versions | |----------------|-------------------------------| -| `main` | 6.8.3–6.9.3 | -| `gradle70` | 7.0 | -| `gradle71` | 7.1-7.3 | -| `gradle74` | 7.4 | -| `gradle75` | 7.5 | -| `gradle76` | 7.6 | +| `main` | 7.6.0–7.6.3 | | `gradle80` | 8.0 | | `gradle81` | 8.1.1 | | `gradle82` | 8.2.1–8.4 | From 594c2a25939635a4609da62f68b7d9f7e071692c Mon Sep 17 00:00:00 2001 From: Sarah Haggarty Date: Fri, 25 Oct 2024 10:14:25 +0200 Subject: [PATCH 03/32] chore: update Gradle versions for 2.1.0 --- docs/topics/gradle/gradle-compiler-options.md | 16 +++++++------- .../topics/gradle/gradle-configure-project.md | 7 +++--- docs/topics/maven.md | 22 +++++++++---------- .../multiplatform-dsl-reference.md | 8 +++---- docs/v.list | 8 +++---- 5 files changed, 31 insertions(+), 30 deletions(-) diff --git a/docs/topics/gradle/gradle-compiler-options.md b/docs/topics/gradle/gradle-compiler-options.md index 91d79ecbfb7..56664217f45 100644 --- a/docs/topics/gradle/gradle-compiler-options.md +++ b/docs/topics/gradle/gradle-compiler-options.md @@ -177,14 +177,14 @@ Here is a complete list of options for Gradle tasks: ### Attributes common to JVM and JS -| Name | Description | Possible values |Default value | -|------|-------------|-----------------|--------------| -| `allWarningsAsErrors` | Report an error if there are any warnings | | false | -| `suppressWarnings` | Don't generate warnings | | false | -| `verbose` | Enable verbose logging output. Works only when the [Gradle debug log level enabled](https://docs.gradle.org/current/userguide/logging.html) | | false | -| `freeCompilerArgs` | A list of additional compiler arguments. You can use experimental `-X` arguments here too. See an [example](#example-of-additional-arguments-usage-via-freecompilerargs) | | [] | -| `apiVersion` | Restrict the use of declarations to those from the specified version of bundled libraries | "1.6", "1.7", "1.8", "1.9", "2.0", "2.1" (EXPERIMENTAL) | | -| `languageVersion` | Provide source compatibility with the specified version of Kotlin | "1.6", "1.7", "1.8", "1.9", "2.0", "2.1" (EXPERIMENTAL) | | +| Name | Description | Possible values |Default value | +|------|-------------|----------------------------------------------------------------|--------------| +| `allWarningsAsErrors` | Report an error if there are any warnings | | false | +| `suppressWarnings` | Don't generate warnings | | false | +| `verbose` | Enable verbose logging output. Works only when the [Gradle debug log level enabled](https://docs.gradle.org/current/userguide/logging.html) | | false | +| `freeCompilerArgs` | A list of additional compiler arguments. You can use experimental `-X` arguments here too. See an [example](#example-of-additional-arguments-usage-via-freecompilerargs) | | [] | +| `apiVersion` | Restrict the use of declarations to those from the specified version of bundled libraries | "1.8", "1.9", "2.0", "2.1", "2.2" (EXPERIMENTAL) | | +| `languageVersion` | Provide source compatibility with the specified version of Kotlin | "1.8", "1.9", "2.0", "2.1", "2.2" (EXPERIMENTAL) | | > We are going to deprecate the attribute `freeCompilerArgs` in future releases. If you miss some option in the Kotlin Gradle DSL, diff --git a/docs/topics/gradle/gradle-configure-project.md b/docs/topics/gradle/gradle-configure-project.md index 016b8f020dc..391668f4752 100644 --- a/docs/topics/gradle/gradle-configure-project.md +++ b/docs/topics/gradle/gradle-configure-project.md @@ -50,7 +50,8 @@ In the following table, there are the minimum and maximum **fully supported** ve | KGP version | Gradle min and max versions | AGP min and max versions | |---------------|----------------------------------------|-----------------------------------------------------| -| 2.0.20 | %minGradleVersion%–%maxGradleVersion%* | %minAndroidGradleVersion%–%maxAndroidGradleVersion% | +| 2.1.0 | %minGradleVersion%–%maxGradleVersion%* | %minAndroidGradleVersion%–%maxAndroidGradleVersion% | +| 2.0.20–2.0.21 | 6.8.3–8.8* | 7.1.3–8.5 | | 2.0.0 | 6.8.3–8.5 | 7.1.3–8.3.1 | | 1.9.20–1.9.25 | 6.8.3–8.1.1 | 4.2.2–8.1.0 | | 1.9.0–1.9.10 | 6.8.3–7.6.0 | 4.2.2–7.4.0 | @@ -60,8 +61,8 @@ In the following table, there are the minimum and maximum **fully supported** ve | 1.7.0–1.7.10 | 6.7.1–7.0.2 | 3.4.3–7.0.2 | | 1.6.20–1.6.21 | 6.1.1–7.0.2 | 3.4.3–7.0.2 | -> *Kotlin 2.0.20 is fully compatible with Gradle 6.8.3 through 8.6. -> Gradle 8.7 and 8.8 are also supported, with only one exception: If you use the Kotlin Multiplatform Gradle plugin, +> *Kotlin 2.0.20–2.0.21 and Kotlin 2.1.0 are fully compatible with Gradle up to 8.6. +> Gradle versions 8.7–8.10 are also supported, with only one exception: If you use the Kotlin Multiplatform Gradle plugin, > you may see deprecation warnings in your multiplatform projects calling the [`withJava()` function in the JVM target](multiplatform-dsl-reference.md#jvm-targets). > For more information, see the issue in [YouTrack](https://youtrack.jetbrains.com/issue/KT-66542/Gradle-JVM-target-with-withJava-produces-a-deprecation-warning). > diff --git a/docs/topics/maven.md b/docs/topics/maven.md index 12832283f21..8df129b8e23 100644 --- a/docs/topics/maven.md +++ b/docs/topics/maven.md @@ -322,17 +322,17 @@ The following attributes are supported: ### Attributes specific to JVM -| Name | Property name | Description | Possible values | Default value | -|-------------------|---------------------------------|------------------------------------------------------------------------------------------------------|---------------------------------------------------------|-----------------------------| -| `nowarn` | | Generate no warnings | true, false | false | -| `languageVersion` | kotlin.compiler.languageVersion | Provide source compatibility with the specified version of Kotlin | "1.6", "1.7", "1.8", "1.9", "2.0", "2.1" (EXPERIMENTAL) | | -| `apiVersion` | kotlin.compiler.apiVersion | Allow using declarations only from the specified version of bundled libraries | "1.6", "1.7", "1.8", "1.9", "2.0", "2.1" (EXPERIMENTAL) | | -| `sourceDirs` | | The directories containing the source files to compile | | The project source roots | -| `compilerPlugins` | | Enabled compiler plugins | | [] | -| `pluginOptions` | | Options for compiler plugins | | [] | -| `args` | | Additional compiler arguments | | [] | -| `jvmTarget` | `kotlin.compiler.jvmTarget` | Target version of the generated JVM bytecode | "1.8", "9", "10", ..., "22" | "%defaultJvmTargetVersion%" | -| `jdkHome` | `kotlin.compiler.jdkHome` | Include a custom JDK from the specified location into the classpath instead of the default JAVA_HOME | | | +| Name | Property name | Description | Possible values | Default value | +|-------------------|---------------------------------|------------------------------------------------------------------------------------------------------|--------------------------------------------------|-----------------------------| +| `nowarn` | | Generate no warnings | true, false | false | +| `languageVersion` | kotlin.compiler.languageVersion | Provide source compatibility with the specified version of Kotlin | "1.8", "1.9", "2.0", "2.1", "2.2" (EXPERIMENTAL) | | +| `apiVersion` | kotlin.compiler.apiVersion | Allow using declarations only from the specified version of bundled libraries | "1.8", "1.9", "2.0", "2.1", "2.2" (EXPERIMENTAL) | | +| `sourceDirs` | | The directories containing the source files to compile | | The project source roots | +| `compilerPlugins` | | Enabled compiler plugins | | [] | +| `pluginOptions` | | Options for compiler plugins | | [] | +| `args` | | Additional compiler arguments | | [] | +| `jvmTarget` | `kotlin.compiler.jvmTarget` | Target version of the generated JVM bytecode | "1.8", "9", "10", ..., "22" | "%defaultJvmTargetVersion%" | +| `jdkHome` | `kotlin.compiler.jdkHome` | Include a custom JDK from the specified location into the classpath instead of the default JAVA_HOME | | | ## Use BOM diff --git a/docs/topics/multiplatform/multiplatform-dsl-reference.md b/docs/topics/multiplatform/multiplatform-dsl-reference.md index 7308190d1d2..9320d43e89e 100644 --- a/docs/topics/multiplatform/multiplatform-dsl-reference.md +++ b/docs/topics/multiplatform/multiplatform-dsl-reference.md @@ -941,8 +941,8 @@ The `languageSettings {}` block of a source set defines certain aspects of proje kotlin { sourceSets.all { languageSettings.apply { - languageVersion = "%languageVersion%" // possible values: '1.6', '1.7', '1.8', '1.9', `2.0` - apiVersion = "%apiVersion%" // possible values: '1.6', '1.7', '1.8', '1.9', `2.0` + languageVersion = "%languageVersion%" // possible values: '1.8', '1.9', `2.0`, `2.1` + apiVersion = "%apiVersion%" // possible values: '1.8', '1.9', `2.0`, `2.1` enableLanguageFeature("InlineClasses") // language feature name optIn("kotlin.ExperimentalUnsignedTypes") // annotation FQ-name progressiveMode = true // false by default @@ -958,8 +958,8 @@ kotlin { kotlin { sourceSets.all { languageSettings { - languageVersion = '%languageVersion%' // possible values: '1.6', '1.7', '1.8', '1.9', `2.0` - apiVersion = '%apiVersion%' // possible values: '1.6', '1.7', '1.8', '1.9', `2.0` + languageVersion = '%languageVersion%' // possible values: '1.8', '1.9', `2.0`, `2.1` + apiVersion = '%apiVersion%' // possible values: '1.8', '1.9', `2.0`, `2.1` enableLanguageFeature('InlineClasses') // language feature name optIn('kotlin.ExperimentalUnsignedTypes') // annotation FQ-name progressiveMode = true // false by default diff --git a/docs/v.list b/docs/v.list index f7aa5b916d6..dd88fdf7f89 100644 --- a/docs/v.list +++ b/docs/v.list @@ -31,13 +31,13 @@ - - + + - + - + From f88f0df7b27cce347a2d8b18116ff64cda8ba36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Csorba?= <150924204+daniCsorbaJB@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:12:56 +0100 Subject: [PATCH 04/32] update: adding information about new wasm jsexception handling (#4518) * update: adding information about new wasm jsexception handling * implementing comments for TWr review --- docs/topics/wasm/wasm-js-interop.md | 53 +++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/docs/topics/wasm/wasm-js-interop.md b/docs/topics/wasm/wasm-js-interop.md index a88b204bb7c..168aae43d95 100644 --- a/docs/topics/wasm/wasm-js-interop.md +++ b/docs/topics/wasm/wasm-js-interop.md @@ -359,7 +359,56 @@ external fun processData(data: JsArray): T ## Exception handling -The Kotlin `try-catch` expression can't catch JavaScript exceptions. +You can use Kotlin `try-catch` expression to catch JavaScript exceptions. +However, accessing specific details about the thrown value in Kotlin/Wasm isn’t possible by default. + +You can configure the `JsException` type to include the original error message and stack trace from JavaScript. +To do so, add the following compiler flag to your `build.gradle.kts` file: + +```kotlin +kotlin { + wasmJs { + compilerOptions { + freeCompilerArgs.add("-Xwasm-attach-js-exception") + } + } +} +``` + +This behavior depends on the `WebAssembly.JSTag` API, which is only available in certain browsers: + +* **Chrome:** Supported from version 115 +* **Firefox:** Supported from version 129 +* **Safari:** Not yet supported + +Here’s an example demonstrating this behavior: + +```kotlin +external object JSON { + fun parse(json: String): T +} + +fun main() { + try { + JSON.parse("an invalid JSON") + } catch (e: JsException) { + println("Thrown value is: ${e.thrownValue}") + // SyntaxError: Unexpected token 'a', "an invalid JSON" is not valid JSON + + println("Message: ${e.message}") + // Message: Unexpected token 'a', "an invalid JSON" is not valid JSON + + println("Stacktrace:") + // Stacktrace: + + // Prints the full JavaScript stack trace + e.printStackTrace() + } +} +``` + +With the `-Xwasm-attach-js-exception` flag enabled, the `JsException` type provides specific details from the JavaScript error. +Without the flag, `JsException` includes only a generic message stating that an exception was thrown while running JavaScript code. If you try to use a JavaScript `try-catch` expression to catch Kotlin/Wasm exceptions, it looks like a generic `WebAssembly.Exception` without directly accessible messages and data. @@ -381,7 +430,7 @@ Although Kotlin/Wasm interoperability shares similarities with Kotlin/JS interop | **Long** | Type corresponds to JavaScript `BigInt`. | Visible as a custom class in JavaScript. | | **Arrays** | Not supported in interop directly yet. You can use the new `JsArray` type instead. | Implemented as JavaScript arrays. | | **Other types** | Requires `JsReference<>` to pass Kotlin objects to JavaScript. | Allows the use of non-external Kotlin class types in external declarations. | -| **Exception handling** | Starting from Kotlin 2.0.0, it can catch any JavaScript exception via the `JsException` and `Throwable` types. | Can catch JavaScript `Error` via the `Throwable` type. It can catch any JavaScript exception using the `dynamic` type. | +| **Exception handling** | You can catch any JavaScript exception with the `JsException` and `Throwable` types. | Can catch JavaScript `Error` using the `Throwable` type. It can catch any JavaScript exception using the `dynamic` type. | | **Dynamic types** | Does not support the `dynamic` type. Use `JsAny` instead (see sample code below). | Supports the `dynamic` type. | > Kotlin/JS [dynamic type](dynamic-type.md) for interoperability with untyped or loosely typed objects is not From e8504c1a3d962007f5db951f6ab9beb25aff8859 Mon Sep 17 00:00:00 2001 From: Sarah Haggarty Date: Mon, 4 Nov 2024 16:10:14 +0100 Subject: [PATCH 05/32] chore: fix opt-in links --- docs/topics/whatsnew1530.md | 2 +- docs/topics/whatsnew17.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/topics/whatsnew1530.md b/docs/topics/whatsnew1530.md index a6b41372b01..894ed017794 100644 --- a/docs/topics/whatsnew1530.md +++ b/docs/topics/whatsnew1530.md @@ -154,7 +154,7 @@ The feature has the following restrictions: > {style="warning"} -The author of a library can mark an experimental API as [requiring opt-in](opt-in-requirements.md#create-opt-in-requirement-annotations) to inform users about its experimental state. The compiler raises a warning or error when the API is used and requires [explicit consent](opt-in-requirements.md#opt-in-to-using-api) to suppress it. +The author of a library can mark an experimental API as [requiring opt-in](opt-in-requirements.md#create-opt-in-requirement-annotations) to inform users about its experimental state. The compiler raises a warning or error when the API is used and requires [explicit consent](opt-in-requirements.md#opt-in-to-api) to suppress it. In Kotlin 1.5.30, the compiler treats any declaration that has an experimental type in the signature as experimental. Namely, it requires opt-in even for implicit usages of an experimental API. For example, if the function's return type is marked as an experimental API element, a usage of the function requires you to opt-in even if the declaration is not marked as requiring an opt-in explicitly. diff --git a/docs/topics/whatsnew17.md b/docs/topics/whatsnew17.md index b4f53d46ec5..a2ea0a10ed4 100644 --- a/docs/topics/whatsnew17.md +++ b/docs/topics/whatsnew17.md @@ -157,7 +157,7 @@ additional compiler configuration. Before 1.7.0, the opt-in feature itself required the argument `-opt-in=kotlin.RequiresOptIn` to avoid a warning. It no longer requires this; however, you can still use the compiler argument `-opt-in` to opt-in for other -annotations, [module-wide](opt-in-requirements.md#module-wide-opt-in). +annotations, [a module](opt-in-requirements.md#opt-in-a-module). ### Stable definitely non-nullable types From 62a5f407fb44bfbfcaaad55a3c4d1727e7539969 Mon Sep 17 00:00:00 2001 From: Sarah Haggarty Date: Mon, 11 Nov 2024 10:46:30 +0100 Subject: [PATCH 06/32] chore: update versions in KMP compat. guide for 2.1.0 --- .../multiplatform-compatibility-guide.md | 17 +++++++++-------- docs/v.list | 4 +++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/topics/multiplatform/multiplatform-compatibility-guide.md b/docs/topics/multiplatform/multiplatform-compatibility-guide.md index d90a6754b69..dd318c25cf3 100644 --- a/docs/topics/multiplatform/multiplatform-compatibility-guide.md +++ b/docs/topics/multiplatform/multiplatform-compatibility-guide.md @@ -13,14 +13,15 @@ developing projects with Kotlin Multiplatform. When configuring your project, check the compatibility of a particular version of the Kotlin Multiplatform Gradle plugin (same as the Kotlin version in your project) with available Gradle, Xcode, and Android Gradle plugin versions: -| Kotlin Multiplatform plugin version | Gradle | Android Gradle plugin | Xcode | -|-------------------------------------|-----------|-----------------------|-------| -| 2.0.21 | 7.5-8.8* | 7.4.2–8.5 | 16.0 | -| 2.0.20 | 7.5-8.8* | 7.4.2–8.5 | 15.3 | -| 2.0.0 | 7.5-8.5 | 7.4.2–8.3 | 15.3 | -| 1.9.20 | 7.5-8.1.1 | 7.4.2–8.2 | 15.0 | - -> Kotlin 2.0.20 and 2.0.21 are fully compatible with Gradle 6.8.3 through 8.6. +| Kotlin Multiplatform plugin version | Gradle | Android Gradle plugin | Xcode | +|-------------------------------------|----------------------------------------|---------------------------------|---------| +| 2.1.0 | %minGradleVersion%–%maxGradleVersion%* | 7.4.2–%maxAndroidGradleVersion% | %xcode% | +| 2.0.21 | 7.5-8.8* | 7.4.2–8.5 | 16.0 | +| 2.0.20 | 7.5-8.8* | 7.4.2–8.5 | 15.3 | +| 2.0.0 | 7.5-8.5 | 7.4.2–8.3 | 15.3 | +| 1.9.20 | 7.5-8.1.1 | 7.4.2–8.2 | 15.0 | + +> *Kotlin 2.0.20–2.0.21 and Kotlin 2.1.0 are fully compatible with Gradle up to 8.6. > Gradle 8.7 and 8.8 are also supported, but you may see deprecation warnings in your multiplatform projects > calling the [`withJava()` function in the JVM target](multiplatform-dsl-reference.md#jvm-targets). > For more information, see the issue in [YouTrack](https://youtrack.jetbrains.com/issue/KT-66542/Gradle-JVM-target-with-withJava-produces-a-deprecation-warning). diff --git a/docs/v.list b/docs/v.list index dd88fdf7f89..c1db0170a1d 100644 --- a/docs/v.list +++ b/docs/v.list @@ -35,7 +35,7 @@ - + @@ -63,4 +63,6 @@ value="1.9.22" type="string"/> + + From b3a3688716cae72dca4651ac35a020e60beaebde Mon Sep 17 00:00:00 2001 From: alejandrapedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:23:52 +0100 Subject: [PATCH 07/32] feat: Guard conditions in when expressions (#4417) * Guard conditions in when expressions This PR adds information about Guard conditions in when expressions and how to use them. * Alejandro's review * chore: adding `is` in a branch Co-authored-by: Alejandro Serrano * chore: adding `data` in code line Co-authored-by: Alejandro Serrano * Andrey review * Update docs/topics/control-flow.md Co-authored-by: Andrey Polyakov * chore: fix code comments --------- Co-authored-by: Alejandro Serrano Co-authored-by: Andrey Polyakov --- docs/topics/coding-conventions.md | 20 +++++++- docs/topics/control-flow.md | 76 +++++++++++++++++++++++++++++++ docs/topics/sealed-classes.md | 3 ++ 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/docs/topics/coding-conventions.md b/docs/topics/coding-conventions.md index 93117481c25..c54fb4720aa 100644 --- a/docs/topics/coding-conventions.md +++ b/docs/topics/coding-conventions.md @@ -980,7 +980,7 @@ For example, use this syntax with `if`: if (x == null) ... else ... ``` -instead of this one with `when`: +Instead of this one with `when`: ```kotlin when (x) { @@ -991,6 +991,24 @@ when (x) { Prefer using `when` if there are three or more options. +### Guard conditions in when expression + +Use parentheses when combining multiple boolean expressions in `when` expressions or statements with [guard conditions](control-flow.md#guard-conditions-in-when-expressions): + +```kotlin +when (status) { + is Status.Ok if (status.info.isEmpty() || status.info.id == null) -> "no information" +} +``` + +Instead of: + +```kotlin +when (status) { + is Status.Ok if status.info.isEmpty() || status.info.id == null -> "no information" +} +``` + ### Nullable Boolean values in conditions If you need to use a nullable `Boolean` in a conditional statement, use `if (value == true)` or `if (value == false)` checks. diff --git a/docs/topics/control-flow.md b/docs/topics/control-flow.md index fff218112c4..7bbc8935a3a 100644 --- a/docs/topics/control-flow.md +++ b/docs/topics/control-flow.md @@ -260,6 +260,82 @@ fun Request.getBody() = The scope of a variable introduced as the subject is restricted to the body of the `when` expression or statement. +### Guard conditions in when expressions + +> Guard conditions are an [experimental feature](components-stability.md#stability-levels-explained) that may be changed at any time. +> We would appreciate your feedback in [YouTrack](https://youtrack.jetbrains.com/issue/KT-71140/Guard-conditions-in-when-expressions-feedback). +> +{style="warning"} + +Guard conditions allow you to include +more than one condition to the branches of a `when` expression, making complex control flow more explicit and concise. +You can use guard conditions in `when` expressions or statements with a subject. + +To include a guard condition in a branch, place it after the primary condition, separated by `if`: + +```kotlin +sealed interface Animal { + data class Cat(val mouseHunter: Boolean) : Animal + data class Dog(val breed: String) : Animal +} + +fun feedAnimal(animal: Animal) { + when (animal) { + // Branch with only primary condition. Calls `feedDog()` when `Animal` is `Dog` + is Animal.Dog -> feedDog() + // Branch with both primary and guard conditions. Calls `feedCat()` when `Animal` is `Cat` and is not `mouseHunter` + is Animal.Cat if !animal.mouseHunter -> feedCat() + // Prints "Unknown animal" if none of the above conditions match + else -> println("Unknown animal") + } +} +``` + +In a single `when` expression, you can combine branches with and without guard conditions. +The code in a branch with a guard condition runs only if both the primary condition and the guard condition evaluate to true. +If the primary condition does not match, the guard condition is not evaluated. + +If you use guard conditions in `when` statements without an `else` branch, and none of the conditions matches, none of the branches is executed. + +Otherwise, if you use guard conditions in `when` expressions without an `else` branch, the compiler requires you to declare all the possible cases to avoid runtime errors. + +Additionally, guard conditions support `else if`: + +```kotlin +when (animal) { + // Checks if `animal` is `Dog` + is Animal.Dog -> feedDog() + // Guard condition that checks if `animal` is `Cat` and not `mouseHunter` + is Animal.Cat if !animal.mouseHunter -> feedCat() + // Calls giveLettuce() if none of the above conditions match and animal.eatsPlants is true + else if animal.eatsPlants -> giveLettuce() + // Prints "Unknown animal" if none of the above conditions match + else -> println("Unknown animal") +} +``` + +Combine multiple guard conditions within a single branch using the boolean operators `&&` (AND) or `||` (OR). +Use parentheses around the boolean expressions to [avoid confusion](coding-conventions.md#guard-conditions-in-when-expression): + +```kotlin +when (animal) { + is Animal.Cat if (!animal.mouseHunter && animal.hungry) -> feedCat() +} +``` + +You can use guard conditions in any `when` expression or statement with a subject, except the case when you have multiple conditions separated by a comma. +For example, `0, 1 -> print("x == 0 or x == 1")`. + +> To enable guard conditions in CLI, run the following command: +> +> `kotlinc -Xwhen-guards main.kt` +> +> To enable guard conditions in Gradle, add the following line to the `build.gradle.kts` file: +> +> `kotlin.compilerOptions.freeCompilerArgs.add("-Xwhen-guards")t` +> +{style="note"} + ## For loops The `for` loop iterates through anything that provides an iterator. This is equivalent to the `foreach` loop in languages like C#. diff --git a/docs/topics/sealed-classes.md b/docs/topics/sealed-classes.md index 9ad424df3b2..9a51d31c76f 100644 --- a/docs/topics/sealed-classes.md +++ b/docs/topics/sealed-classes.md @@ -200,6 +200,9 @@ fun main() { ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.5"} +When using sealed classes with `when` expressions, you can also add guard conditions to include additional checks in a single branch. +For more information, see [Guard conditions in when expressions](control-flow.md#guard-conditions-in-when-expressions). + > In multiplatform projects, if you have a sealed class with a `when` expression as an > [expected declaration](multiplatform-expect-actual.md) in your common code, you still need an `else` branch. > This is because subclasses of `actual` platform implementations may extend sealed classes that From d58940587e54e5da823ca4a0d01ffbeba8251b70 Mon Sep 17 00:00:00 2001 From: Andrey Polyakov Date: Wed, 13 Nov 2024 11:56:39 +0100 Subject: [PATCH 08/32] update: update JVM target bytecode version 23 (#4547) --- docs/topics/faq.md | 2 +- docs/topics/gradle/gradle-compiler-options.md | 2 +- docs/topics/maven.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/topics/faq.md b/docs/topics/faq.md index 6b7d36432dd..3292972bec7 100644 --- a/docs/topics/faq.md +++ b/docs/topics/faq.md @@ -102,7 +102,7 @@ When targeting native, Kotlin will produce platform-specific code (via LLVM). Kotlin lets you choose the version of JVM for execution. By default, the Kotlin/JVM compiler produces Java 8 compatible bytecode. If you want to make use of optimizations available in newer versions of Java, you can explicitly specify the target Java -version from 9 to 21. Note that in this case the resulting bytecode might not run on lower versions. +version from 9 to 23. Note that in this case the resulting bytecode might not run on lower versions. Starting with [Kotlin 1.5](whatsnew15.md#new-default-jvm-target-1-8), the compiler does not support producing bytecode compatible with Java versions below 8. ### Is Kotlin hard? diff --git a/docs/topics/gradle/gradle-compiler-options.md b/docs/topics/gradle/gradle-compiler-options.md index 56664217f45..d27401a9929 100644 --- a/docs/topics/gradle/gradle-compiler-options.md +++ b/docs/topics/gradle/gradle-compiler-options.md @@ -171,7 +171,7 @@ Here is a complete list of options for Gradle tasks: | Name | Description | Possible values | Default value | |---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|-----------------------------| | `javaParameters` | Generate metadata for Java 1.8 reflection on method parameters | | false | -| `jvmTarget` | Target version of the generated JVM bytecode | "1.8", "9", "10", ..., "21", "22". Also, see [Types for compiler options](#types-for-compiler-options) | "%defaultJvmTargetVersion%" | +| `jvmTarget` | Target version of the generated JVM bytecode | "1.8", "9", "10", ..., "22", "23". Also, see [Types for compiler options](#types-for-compiler-options) | "%defaultJvmTargetVersion%" | | `noJdk` | Don't automatically include the Java runtime into the classpath | | false | | `jvmTargetValidationMode` |
  • Validation of the [JVM target compatibility](gradle-configure-project.md#check-for-jvm-target-compatibility-of-related-compile-tasks) between Kotlin and Java
  • A property for tasks of the `KotlinCompile` type.
  • | `WARNING`, `ERROR`, `INFO` | `ERROR` | diff --git a/docs/topics/maven.md b/docs/topics/maven.md index 8df129b8e23..bfbdbc4f9c3 100644 --- a/docs/topics/maven.md +++ b/docs/topics/maven.md @@ -331,7 +331,7 @@ The following attributes are supported: | `compilerPlugins` | | Enabled compiler plugins | | [] | | `pluginOptions` | | Options for compiler plugins | | [] | | `args` | | Additional compiler arguments | | [] | -| `jvmTarget` | `kotlin.compiler.jvmTarget` | Target version of the generated JVM bytecode | "1.8", "9", "10", ..., "22" | "%defaultJvmTargetVersion%" | +| `jvmTarget` | `kotlin.compiler.jvmTarget` | Target version of the generated JVM bytecode | "1.8", "9", "10", ..., "23" | "%defaultJvmTargetVersion%" | | `jdkHome` | `kotlin.compiler.jdkHome` | Include a custom JDK from the specified location into the classpath instead of the default JAVA_HOME | | | ## Use BOM From 84931233ab76927e2037c48abb9e8d69f749a2c2 Mon Sep 17 00:00:00 2001 From: Aleksey Zamulla Date: Thu, 14 Nov 2024 12:47:44 +0100 Subject: [PATCH 09/32] feature: Node.js settings per subproject (#4545) Describing changes from [KT-69628](https://youtrack.jetbrains.com/issue/KT-69628/K-Wasm-Node.js-version-per-project): `NodeJsRootPlugin` deprecated, with settings moved to `NodeJsPlugin`. --------- Co-authored-by: alejandrapedroza <39709865+AlejandraPedroza@users.noreply.github.com> --- docs/topics/js/js-project-setup.md | 77 ++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/docs/topics/js/js-project-setup.md b/docs/topics/js/js-project-setup.md index 357845208f9..d5df9f5479f 100644 --- a/docs/topics/js/js-project-setup.md +++ b/docs/topics/js/js-project-setup.md @@ -595,39 +595,98 @@ each of which defines a mode, as well as [include](https://webpack.js.org/config ## Node.js For Kotlin/JS projects targeting Node.js, the plugin automatically downloads and installs the Node.js environment on the -host. You can also use an existing Node.js instance if you have it. +host. +You can also use an existing Node.js instance if you have it. + +### Configuring Node.js settings + +You can configure Node.js settings for each subproject, or set them for the project as a whole. + +For example, to set the Node.js version for a specific subproject, add the following lines to its Gradle block +in your `build.gradle(.kts)` file: + + + + +```kotlin +project.plugins.withType { + project.the().version = "your Node.js version" +} +``` + + + + +```groovy +project.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin) { + project.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsEnvSpec).version = "your Node.js version" +} +``` + + + + +To set a version for the entire project, including all subprojects, apply the same code to the `allProjects {}` block: + + + + +```kotlin +allprojects { + project.plugins.withType { + project.the().version = "your Node.js version" + } +} +``` + + + + +```groovy +allprojects { + project.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin) { + project.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsEnvSpec).version = "your Node.js version" +} +``` + + + + +> Using the `NodeJsRootPlugin` class to configure Node.js setting for the entire project is deprecated and will eventually +> stop being supported. +> +{style="note"} ### Use pre-installed Node.js If Node.js is already installed on the host where you build Kotlin/JS projects, you can configure the Kotlin Multiplatform Gradle plugin to use it instead of installing its own Node.js instance. -To use the pre-installed Node.js instance, add the following lines to `build.gradle(.kts)`: +To use a pre-installed Node.js instance, add the following lines to your `build.gradle(.kts)` file: ```kotlin -rootProject.plugins.withType { - rootProject.the().download = false - // "true" for default behavior +project.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin) { + // Set to `true` for default behavior + project.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsEnvSpec).download = false } - ``` ```groovy -rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin) { - rootProject.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension).download = false +project.plugins.withType { + // Set to `true` for default behavior + project.the().download = false } ``` - ## Yarn By default, to download and install your declared dependencies at build time, the plugin manages its own instance of the From 2ff23d6a45b52fc8c690730b764a78f1486edd88 Mon Sep 17 00:00:00 2001 From: Sarah Haggarty Date: Mon, 4 Nov 2024 12:04:24 +0100 Subject: [PATCH 10/32] chore: remove experimental status for compilerOptions DSL --- .../multiplatform-configure-compilations.md | 81 ---------------- .../multiplatform-dsl-reference.md | 93 ++++--------------- 2 files changed, 17 insertions(+), 157 deletions(-) diff --git a/docs/topics/multiplatform/multiplatform-configure-compilations.md b/docs/topics/multiplatform/multiplatform-configure-compilations.md index 764f90f8d98..dfe57fceb9d 100644 --- a/docs/topics/multiplatform/multiplatform-configure-compilations.md +++ b/docs/topics/multiplatform/multiplatform-configure-compilations.md @@ -31,42 +31,6 @@ This example configures a compiler option that is common across all targets: ```kotlin kotlin { - targets.all { - compilations.all { - compilerOptions.configure { - allWarningsAsErrors.set(true) - } - } - } -} -``` - - - - -```groovy -kotlin { - targets.all { - compilations.all { - compilerOptions.configure { - allWarningsAsErrors = true - } - } - } -} -``` - - - - -Alternatively, you can use the `compilerOptions {}` [top-level block](multiplatform-dsl-reference.md#top-level-blocks): - - - - -```kotlin -kotlin { - @OptIn(ExperimentalKotlinGradlePluginApi::class) compilerOptions { allWarningsAsErrors.set(true) } @@ -87,52 +51,14 @@ kotlin { -> The support for `compilerOptions {}` as a top-level block is [Experimental](components-stability.md#stability-levels-explained) -> and requires opt-in. It may be dropped or changed at any time. Use it only for evaluation purposes. We would appreciate -> your feedback on it in [YouTrack](https://kotl.in/issue). -> -{style="warning"} - ## Configure compilations for one target -```kotlin -kotlin { - jvm().compilations.all { - compilerOptions.configure { - jvmTarget.set(JvmTarget.JVM_1_8) - } - } -} -``` - - - - -```groovy -kotlin { - jvm().compilations.all { - compilerOptions.configure { - jvmTarget = JvmTarget.JVM_1_8 - } - } -} -``` - - - - -Alternatively, you can use the `compilerOptions {}` block at target level: - - - - ```kotlin kotlin { jvm { - @OptIn(ExperimentalKotlinGradlePluginApi::class) compilerOptions { jvmTarget.set(JvmTarget.JVM_1_8) } @@ -156,13 +82,6 @@ kotlin { -> The support for the `compilerOptions {}` block at target level is [Experimental](components-stability.md#stability-levels-explained) -> and requires opt-in. It may be dropped or changed at any time. Use it only for evaluation purposes. We would appreciate -> your feedback on it in [YouTrack](https://kotl.in/issue). -> -{style="warning"} - - ## Configure one compilation diff --git a/docs/topics/multiplatform/multiplatform-dsl-reference.md b/docs/topics/multiplatform/multiplatform-dsl-reference.md index 9320d43e89e..9ff3a0067a0 100644 --- a/docs/topics/multiplatform/multiplatform-dsl-reference.md +++ b/docs/topics/multiplatform/multiplatform-dsl-reference.md @@ -36,19 +36,13 @@ plugins { `kotlin {}` is the top-level block for multiplatform project configuration in the Gradle build script. Inside `kotlin {}`, you can write the following blocks: -| **Block** | **Description** | -|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| _<targetName>_ | Declares a particular target of a project. The names of available targets are listed in the [Targets](#targets) section. | -| `targets` | All targets of the project. | -| `presets` | All predefined targets. Use this for [configuring multiple predefined targets](#targets) at once. | -| `sourceSets` | Configures predefined and declares custom [source sets](#source-sets) of the project. | -| `compilerOptions` | Extension-level common [compiler options](gradle-compiler-options.md) that are used as defaults for all targets and shared source sets. To use it, add the following opt-in: `@OptIn(ExperimentalKotlinGradlePluginApi::class)` | - -> The support for `compilerOptions {}` as a top-level block is [Experimental](components-stability.md#stability-levels-explained) -> and requires opt-in. It may be dropped or changed at any time. Use it only for evaluation purposes. We would appreciate -> your feedback on it in [YouTrack](https://kotl.in/issue). -> -{style="warning"} +| **Block** | **Description** | +|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------| +| _<targetName>_ | Declares a particular target of a project. The names of available targets are listed in the [Targets](#targets) section. | +| `targets` | All targets of the project. | +| `presets` | All predefined targets. Use this for [configuring multiple predefined targets](#targets) at once. | +| `sourceSets` | Configures predefined and declares custom [source sets](#source-sets) of the project. | +| `compilerOptions` | Extension-level common [compiler options](gradle-compiler-options.md) that are used as defaults for all targets and shared source sets. | ## Targets @@ -134,20 +128,14 @@ Each target can have one or more [compilations](#compilations). In any target block, you can use the following declarations: -| **Name** | **Description** | -|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `attributes` | Attributes used for [disambiguating targets](multiplatform-set-up-targets.md#distinguish-several-targets-for-one-platform) for a single platform. | -| `preset` | The preset that the target has been created from, if any. | -| `platformType` | Designates the Kotlin platform of this target. Available values: `jvm`, `androidJvm`, `js`, `wasm`, `native`, `common`. | -| `artifactsTaskName` | The name of the task that builds the resulting artifacts of this target. | -| `components` | The components used to setup Gradle publications. | -| `compilerOptions` | The [compiler options](gradle-compiler-options.md) used for the target. This declaration overrides any `compilerOptions {}` configured at [top level](multiplatform-dsl-reference.md#top-level-blocks). To use it, add the following opt-in: `@OptIn(ExperimentalKotlinGradlePluginApi::class)` | - -> The support for `compilerOptions {}` as a common target configuration is [Experimental](components-stability.md#stability-levels-explained) -> and requires opt-in. It may be dropped or changed at any time. Use it only for evaluation purposes. We would appreciate -> your feedback on it in [YouTrack](https://kotl.in/issue). -> -{style="warning"} +| **Name** | **Description** | +|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `attributes` | Attributes used for [disambiguating targets](multiplatform-set-up-targets.md#distinguish-several-targets-for-one-platform) for a single platform. | +| `preset` | The preset that the target has been created from, if any. | +| `platformType` | Designates the Kotlin platform of this target. Available values: `jvm`, `androidJvm`, `js`, `wasm`, `native`, `common`. | +| `artifactsTaskName` | The name of the task that builds the resulting artifacts of this target. | +| `components` | The components used to setup Gradle publications. | +| `compilerOptions` | The [compiler options](gradle-compiler-options.md) used for the target. This declaration overrides any `compilerOptions {}` configured at [top level](multiplatform-dsl-reference.md#top-level-blocks). | ### JVM targets @@ -756,12 +744,8 @@ kotlin { } // Configure all compilations of all targets: - targets.all { - compilations.all { - compilerOptions.configure { - allWarningsAsErrors.set(true) - } - } + compilerOptions { + allWarningsAsErrors.set(true) } } ``` @@ -782,42 +766,6 @@ kotlin { compilations.test.runtimeDependencyFiles // get the test runtime classpath } - // Configure all compilations of all targets: - targets.all { - compilations.all { - compilerOptions.configure { - allWarningsAsErrors = true - } - } - } -} -``` - - - - -Alternatively, to configure compiler options that are common for all targets, you can use the `compilerOptions {}` [top-level block](multiplatform-dsl-reference.md#top-level-blocks): - - - - -```kotlin -kotlin { - - // Configure all compilations of all targets: - @OptIn(ExperimentalKotlinGradlePluginApi::class) - compilerOptions { - allWarningsAsErrors.set(true) - } -} -``` - - - - -```groovy -kotlin { - // Configure all compilations of all targets: compilerOptions { allWarningsAsErrors = true @@ -828,13 +776,6 @@ kotlin { -> The support for `compilerOptions {}` as a top-level block is [Experimental](components-stability.md#stability-levels-explained) -> and requires opt-in. It may be dropped or changed at any time. Use it only for evaluation purposes. We would appreciate -> your feedback on it in [YouTrack](https://kotl.in/issue). -> -{style="warning"} - - ## Dependencies The `dependencies {}` block of the source set declaration contains the dependencies of this source set. From 4ace0b59ffc87552d6eae0b98b431d0b5aed4f5f Mon Sep 17 00:00:00 2001 From: Sarah Haggarty Date: Mon, 4 Nov 2024 13:49:14 +0100 Subject: [PATCH 11/32] update: add new KMP compiler options DSL --- .../multiplatform-dsl-reference.md | 193 ++++++++++++++++-- 1 file changed, 175 insertions(+), 18 deletions(-) diff --git a/docs/topics/multiplatform/multiplatform-dsl-reference.md b/docs/topics/multiplatform/multiplatform-dsl-reference.md index 9ff3a0067a0..87fa6777833 100644 --- a/docs/topics/multiplatform/multiplatform-dsl-reference.md +++ b/docs/topics/multiplatform/multiplatform-dsl-reference.md @@ -36,18 +36,18 @@ plugins { `kotlin {}` is the top-level block for multiplatform project configuration in the Gradle build script. Inside `kotlin {}`, you can write the following blocks: -| **Block** | **Description** | -|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------| -| _<targetName>_ | Declares a particular target of a project. The names of available targets are listed in the [Targets](#targets) section. | -| `targets` | All targets of the project. | -| `presets` | All predefined targets. Use this for [configuring multiple predefined targets](#targets) at once. | -| `sourceSets` | Configures predefined and declares custom [source sets](#source-sets) of the project. | -| `compilerOptions` | Extension-level common [compiler options](gradle-compiler-options.md) that are used as defaults for all targets and shared source sets. | +| **Block** | **Description** | +|----------------------|------------------------------------------------------------------------------------------------------------------------------------------| +| _<targetName>_ | Declares a particular target of a project. The names of available targets are listed in the [Targets](#targets) section. | +| `targets` | Lists all targets of the project. | +| `sourceSets` | Configures predefined and declares custom [source sets](#source-sets) of the project. | +| `compilerOptions` | Specifies common extension-level [compiler options](#compiler-options) that are used as defaults for all targets and shared source sets. | ## Targets -_Target_ is a part of the build responsible for compiling, testing, and packaging a piece of software aimed at -one of the supported platforms. Kotlin provides target presets for each platform. See how to [use a target preset](multiplatform-set-up-targets.md). +A _target_ is a part of the build responsible for compiling, testing, and packaging a piece of software aimed at +one of the supported platforms. Kotlin provides targets for each platform, so you can instruct Kotlin to compile code for +that specific target. To learn more about how to configure these targets, see [Set up targets for Kotlin Multiplatform](multiplatform-set-up-targets.md). Each target can have one or more [compilations](#compilations). In addition to default compilations for test and production purposes, you can [create custom compilations](multiplatform-configure-compilations.md#create-a-custom-compilation). @@ -58,7 +58,7 @@ The complete list of available targets is the following: - + @@ -128,14 +128,13 @@ Each target can have one or more [compilations](#compilations). In any target block, you can use the following declarations: -| **Name** | **Description** | -|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `attributes` | Attributes used for [disambiguating targets](multiplatform-set-up-targets.md#distinguish-several-targets-for-one-platform) for a single platform. | -| `preset` | The preset that the target has been created from, if any. | -| `platformType` | Designates the Kotlin platform of this target. Available values: `jvm`, `androidJvm`, `js`, `wasm`, `native`, `common`. | -| `artifactsTaskName` | The name of the task that builds the resulting artifacts of this target. | -| `components` | The components used to setup Gradle publications. | -| `compilerOptions` | The [compiler options](gradle-compiler-options.md) used for the target. This declaration overrides any `compilerOptions {}` configured at [top level](multiplatform-dsl-reference.md#top-level-blocks). | +| **Name** | **Description** | +|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `attributes` | Attributes used to [distinguish between targets](multiplatform-set-up-targets.md#distinguish-several-targets-for-one-platform) for a single platform. | +| `platformType` | The Kotlin platform for this target. Available values: `jvm`, `androidJvm`, `js`, `wasm`, `native`, `common`. | +| `artifactsTaskName` | The name of the task that builds the resulting artifacts of this target. | +| `components` | Components used to set up Gradle publications. | +| `compilerOptions` | [Compiler options](#compiler-options) used for the target. This declaration overrides any `compilerOptions {}` configured at [top level](multiplatform-dsl-reference.md#top-level-blocks). | ### JVM targets @@ -776,6 +775,164 @@ kotlin { +## Compiler options + +You can configure compiler options in your projects at three different levels: + +* **Extension level**, in the `kotlin {}` block. +* **Target level**, in a target block. +* **Compilation unit level**, usually in a specific compilation task. + +![Kotlin compiler options levels](compiler-options-levels.svg){width=700} + +Settings at a higher level work as defaults for the level below: + +* Compiler options set at the extension level are the default for target-level options, including shared source sets like `commonMain`, `nativeMain`, and `commonTest`. +* Compiler options set at the target level are the default for options at the compilation unit (task) level, like `compileKotlinJvm` and `compileTestKotlinJvm` tasks. + +Configurations made at a lower level override similar settings at higher levels: + +* Task-level compiler options override similar settings at the target or extension level. +* Target-level compiler options override similar settings at the extension level. + +For the list of possible compiler options, see [All compiler options](gradle-compiler-options.md#all-compiler-options). + +### Extension level + +To configure compiler options for all targets in your project, use the `compilerOptions {}` block at the top level: + + + + +```kotlin +kotlin { + // Configures all compilations of all targets + compilerOptions { + allWarningsAsErrors.set(true) + } +} +``` + + + + +```groovy +kotlin { + // Configures all compilations of all targets: + compilerOptions { + allWarningsAsErrors = true + } +} +``` + + + + +### Target level + +To configure compiler options for a specific target in your project, use the `compilerOptions {}` block inside the target block: + + + + +```kotlin +kotlin { + jvm { + // Configures all compilations of the JVM target + compilerOptions { + allWarningsAsErrors.set(true) + } + } +} +``` + + + + +```groovy +kotlin { + jvm { + // Configures all compilations of the JVM target + compilerOptions { + allWarningsAsErrors = true + } + } +} +``` + + + + +### Compilation unit level + +To configure compiler options for a specific task, use the `compilerOptions {}` block inside the task: + + + + +```kotlin +task.named("compileKotlinJvm") { + compilerOptions { + allWarningsAsErrors.set(true) + } +} +``` + + + + +```groovy +task.named("compileKotlinJvm") { + compilerOptions { + allWarningsAsErrors = true + } +} +``` + + + + +To configure compiler options for a specific compilation, use the `compilerOptions {}` block within the compilation's task provider: + + + + +```kotlin +kotlin { + jvm { + compilations.named(KotlinCompilation.MAIN_COMPILATION_NAME) { + compileTaskProvider.configure { + // Configures the 'main' compilation: + compilerOptions { + allWarningsAsErrors.set(true) + } + } + } + } +} +``` + + + + +```groovy +kotlin { + jvm { + compilations.named(KotlinCompilation.MAIN_COMPILATION_NAME) { + compileTaskProvider.configure { + // Configures the 'main' compilation: + compilerOptions { + allWarningsAsErrors = true + } + } + } + } +} +``` + + + + ## Dependencies The `dependencies {}` block of the source set declaration contains the dependencies of this source set. From f1ea335f21533ea020ab4d6c25e9a836d5e4ff9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Csorba?= <150924204+daniCsorbaJB@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:51:12 +0100 Subject: [PATCH 12/32] update: adding information about different classpath snapshots (#4525) * update: adding information about different classpath snapshots * implementing developer review comments * implementing Ale's comments --- .../gradle/gradle-compilation-and-caches.md | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/topics/gradle/gradle-compilation-and-caches.md b/docs/topics/gradle/gradle-compilation-and-caches.md index a7080e944f3..afcf5dd0cf0 100644 --- a/docs/topics/gradle/gradle-compilation-and-caches.md +++ b/docs/topics/gradle/gradle-compilation-and-caches.md @@ -13,11 +13,21 @@ On this page, you can learn about the following topics: ## Incremental compilation -The Kotlin Gradle plugin supports incremental compilation. Incremental compilation tracks changes to files in the classpath -between builds so that only the files affected by these changes are compiled. Incremental compilation works with [Gradle's -build cache](#gradle-build-cache-support) and supports [compilation avoidance](https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_compile_avoidance). - -Incremental compilation is supported for Kotlin/JVM and Kotlin/JS projects, and is enabled by default. +The Kotlin Gradle plugin supports incremental compilation, which is enabled by default for Kotlin/JVM and Kotlin/JS projects. +Incremental compilation tracks changes to files in the classpath between builds so that only the files affected +by these changes are compiled. +This approach works with [Gradle's build cache](#gradle-build-cache-support) and supports [compilation avoidance](https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_compile_avoidance). + +For Kotlin/JVM, incremental compilation relies on classpath snapshots, +which capture the API structure of modules to determine when recompilation is necessary. +To optimize the overall pipeline, the Kotlin compiler uses two types of classpath snapshots: + +* **Fine-grained snapshots:** include detailed information about class members, such as properties or functions. +When member-level changes are detected, the Kotlin compiler recompiles only the classes that depend on the modified members. +To maintain performance, the Kotlin Gradle plugin creates coarse-grained snapshots for `.jar` files in the Gradle cache. +* **Coarse-grained snapshots:** only contain the class [ABI](https://en.wikipedia.org/wiki/Application_binary_interface) hash. +When a part of ABI changes, the Kotlin compiler recompiles all classes that depend on the changed class. +This is useful for classes that change infrequently, such as external libraries. > Kotlin/JS projects use a different incremental compilation approach based on history files. > @@ -31,14 +41,14 @@ There are several ways to disable incremental compilation: The parameter should be added to each subsequent build. -Note: Any build with incremental compilation disabled invalidates incremental caches. The first build is never incremental. +When you disable incremental compilation, incremental caches become invalid after the build. The first build is never incremental. > Sometimes problems with incremental compilation become visible several rounds after the failure occurs. Use [build reports](#build-reports) > to track the history of changes and compilations. This can help you to provide reproducible bug reports. > {style="tip"} -If you'd like to learn more about how our current incremental compilation approach works and compares to the previous one, +To learn more about how our current incremental compilation approach works and compares to the previous one, see our [blog post](https://blog.jetbrains.com/kotlin/2022/07/a-new-approach-to-incremental-compilation-in-kotlin/). ### Precise backup of compilation tasks' outputs From 49e38909235174b21b5ad23f0ac07e403bb1e641 Mon Sep 17 00:00:00 2001 From: Sarah Haggarty Date: Mon, 11 Nov 2024 14:27:41 +0100 Subject: [PATCH 13/32] chore: sync KMP compat. guide duplicate targets content --- docs/topics/multiplatform/multiplatform-compatibility-guide.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/topics/multiplatform/multiplatform-compatibility-guide.md b/docs/topics/multiplatform/multiplatform-compatibility-guide.md index dd318c25cf3..dc47d7fc4d4 100644 --- a/docs/topics/multiplatform/multiplatform-compatibility-guide.md +++ b/docs/topics/multiplatform/multiplatform-compatibility-guide.md @@ -764,7 +764,6 @@ Here's the planned deprecation cycle: * 1.9.20: introduce a deprecation warning when multiple similar targets are used in Kotlin Multiplatform projects * 2.1.0: report an error in such cases, except for Kotlin/JS targets; to learn more about this exception, see the issue in [YouTrack](https://youtrack.jetbrains.com/issue/KT-47038/KJS-MPP-Split-JS-target-into-JsBrowser-and-JsNode) - ## Deprecated jvmWithJava preset From 14be51d389fb3ee92239d3a55a9dfab05cbfcf3c Mon Sep 17 00:00:00 2001 From: Sarah Haggarty Date: Mon, 18 Nov 2024 09:57:35 +0100 Subject: [PATCH 14/32] update: add Gradle's isolated projects feature --- .../multiplatform-configure-compilations.md | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/docs/topics/multiplatform/multiplatform-configure-compilations.md b/docs/topics/multiplatform/multiplatform-configure-compilations.md index dfe57fceb9d..d03f3a84aaa 100644 --- a/docs/topics/multiplatform/multiplatform-configure-compilations.md +++ b/docs/topics/multiplatform/multiplatform-configure-compilations.md @@ -371,4 +371,31 @@ Language settings are checked for consistency in the following ways: * `jvmMain` should enable all unstable language features that `commonMain` enables (there's no such requirement for bugfix features). * `jvmMain` should use all experimental annotations that `commonMain` uses. -* `apiVersion`, bugfix language features, and `progressiveMode` can be set arbitrarily. \ No newline at end of file +* `apiVersion`, bugfix language features, and `progressiveMode` can be set arbitrarily. + +## Configure Isolated Projects feature in Gradle + +> This feature is [Experimental](components-stability.md#stability-levels-explained) and is currently in a pre-alpha state with Gradle. +> Use it only with Gradle versions 8.10 or higher, and solely for evaluation purposes. The feature may be dropped or changed at any time. +> We would appreciate your feedback on it in [YouTrack](https://youtrack.jetbrains.com/issue/KT-57279/Support-Gradle-Project-Isolation-Feature-for-Kotlin-Multiplatform). +> Opt-in is required (see details below). +> +{style="warning"} + +Gradle provides the [Isolated Projects](https://docs.gradle.org/current/userguide/isolated_projects.html) feature, +which improves build performance by "isolating" individual projects from each other. The feature separates the build scripts +and plugins between projects, allowing them to run safely in parallel. + +To enable this feature, follow Gradle's instructions to [set the system property](https://docs.gradle.org/current/userguide/isolated_projects.html#how_do_i_use_it). + +If you want to check compatibility before enabling Isolated Projects in Gradle, you can test your projects with the new +Kotlin Gradle plugin model. Add the following Gradle property to your `gradle.properties` file: + +```none +kotlin.kmp.isolated-projects.support=enable +``` + +If you decide to enable the Isolated Projects feature later, remember to remove this Gradle property. The Kotlin Gradle plugin +applies and manages this Gradle property directly. + +For more information about the Isolated Projects feature, see [Gradle's documentation](https://docs.gradle.org/current/userguide/isolated_projects.html). \ No newline at end of file From d8b46810a62f6b18685ff8876bd46253a42c60a6 Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Wed, 20 Nov 2024 17:55:39 +0100 Subject: [PATCH 15/32] feat: warning suppression option (#4550) --- docs/topics/compiler-reference.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/topics/compiler-reference.md b/docs/topics/compiler-reference.md index 0c0e7f65138..03001fd6470 100644 --- a/docs/topics/compiler-reference.md +++ b/docs/topics/compiler-reference.md @@ -131,6 +131,14 @@ $ kotlinc @options/compiler.options hello.kt Enable usages of API that [requires opt-in](opt-in-requirements.md) with a requirement annotation with the given fully qualified name. +### -Xsuppress-warning + +Suppresses specific warnings [globally across the whole project](whatsnew-eap.md), for example: + +```bash +kotlinc -Xsuppress-warning=NOTHING_TO_INLINE -Xsuppress-warning=NO_TAIL_CALLS_FOUND main.kt +``` + ## Kotlin/JVM compiler options The Kotlin compiler for JVM compiles Kotlin source files into Java class files. From a16d9d2aa49552af7b4336eb29ae8235d2477489 Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Wed, 20 Nov 2024 17:59:08 +0100 Subject: [PATCH 16/32] update: separate target set up removed (#4555) --- docs/kr.tree | 3 +- .../multiplatform-discover-project.md | 4 +- .../multiplatform-dsl-reference.md | 3 +- .../multiplatform/multiplatform-intro.md | 1 - .../multiplatform-set-up-targets.md | 68 ------------------- docs/topics/native/native-target-support.md | 2 +- docs/topics/whatsnew1720.md | 2 +- redirects/pages.yml | 9 +-- 8 files changed, 9 insertions(+), 83 deletions(-) delete mode 100644 docs/topics/multiplatform/multiplatform-set-up-targets.md diff --git a/docs/kr.tree b/docs/kr.tree index b259088bd3a..0668c704c23 100644 --- a/docs/kr.tree +++ b/docs/kr.tree @@ -119,9 +119,8 @@ - + - diff --git a/docs/topics/multiplatform/multiplatform-discover-project.md b/docs/topics/multiplatform/multiplatform-discover-project.md index aae7a2662bd..632cbc86862 100644 --- a/docs/topics/multiplatform/multiplatform-discover-project.md +++ b/docs/topics/multiplatform/multiplatform-discover-project.md @@ -123,7 +123,7 @@ kotlin { // Source set declaration: sourceSets { commonMain { - // configure the commonMain source set + // Configure the commonMain source set } } } @@ -313,4 +313,4 @@ tutorial](https://www.jetbrains.com/help/kotlin-multiplatform-dev/multiplatform- * [Learn more about declaring and using predefined source sets in Gradle scripts](multiplatform-hierarchy.md) * [Explore advanced concepts of the multiplatform project structure](multiplatform-advanced-project-structure.md) -* [Learn how to configure compilations](multiplatform-configure-compilations.md) +* [Learn more about target compilation and creating custom compilations](multiplatform-configure-compilations.md) diff --git a/docs/topics/multiplatform/multiplatform-dsl-reference.md b/docs/topics/multiplatform/multiplatform-dsl-reference.md index 87fa6777833..a5d824c30b9 100644 --- a/docs/topics/multiplatform/multiplatform-dsl-reference.md +++ b/docs/topics/multiplatform/multiplatform-dsl-reference.md @@ -47,7 +47,7 @@ Inside `kotlin {}`, you can write the following blocks: A _target_ is a part of the build responsible for compiling, testing, and packaging a piece of software aimed at one of the supported platforms. Kotlin provides targets for each platform, so you can instruct Kotlin to compile code for -that specific target. To learn more about how to configure these targets, see [Set up targets for Kotlin Multiplatform](multiplatform-set-up-targets.md). +that specific target. Learn more about [setting up targets](multiplatform-discover-project.md#targets). Each target can have one or more [compilations](#compilations). In addition to default compilations for test and production purposes, you can [create custom compilations](multiplatform-configure-compilations.md#create-a-custom-compilation). @@ -130,7 +130,6 @@ In any target block, you can use the following declarations: | **Name** | **Description** | |---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `attributes` | Attributes used to [distinguish between targets](multiplatform-set-up-targets.md#distinguish-several-targets-for-one-platform) for a single platform. | | `platformType` | The Kotlin platform for this target. Available values: `jvm`, `androidJvm`, `js`, `wasm`, `native`, `common`. | | `artifactsTaskName` | The name of the task that builds the resulting artifacts of this target. | | `components` | Components used to set up Gradle publications. | diff --git a/docs/topics/multiplatform/multiplatform-intro.md b/docs/topics/multiplatform/multiplatform-intro.md index f4b62e000e7..fbfe6e69ea7 100644 --- a/docs/topics/multiplatform/multiplatform-intro.md +++ b/docs/topics/multiplatform/multiplatform-intro.md @@ -25,7 +25,6 @@ and the target-specific source set. For more details on this topic, see: * [The basics of Kotlin Multiplatform project structure](multiplatform-discover-project.md) -* [Set up targets manually](multiplatform-set-up-targets.md) * [Advanced concepts of the multiplatform project structure](multiplatform-advanced-project-structure.md) ## Use code sharing mechanisms diff --git a/docs/topics/multiplatform/multiplatform-set-up-targets.md b/docs/topics/multiplatform/multiplatform-set-up-targets.md deleted file mode 100644 index 2d744defd11..00000000000 --- a/docs/topics/multiplatform/multiplatform-set-up-targets.md +++ /dev/null @@ -1,68 +0,0 @@ -[//]: # (title: Set up targets for Kotlin Multiplatform) - -You can add targets when creating a project with the [project wizard](https://kmp.jetbrains.com/). If you need to add a target -later, you can do this manually using target presets for [supported platforms](multiplatform-dsl-reference.md#targets). - -Learn more about [additional settings for targets](multiplatform-dsl-reference.md#common-target-configuration). - -```kotlin -kotlin { - jvm() // Create a JVM target with the default name 'jvm' - - linuxX64() { - /* Specify additional settings for the 'linux' target here */ - } -} -``` - -Each target can have one or more [compilations](multiplatform-configure-compilations.md). In addition to default compilations for -test and production purposes, you can [create custom compilations](multiplatform-configure-compilations.md#create-a-custom-compilation). - -## Distinguish several targets for one platform - -You can have several targets for one platform in a multiplatform library. For example, these targets can provide the same -API but use different libraries during runtime, such as testing frameworks and logging solutions. Dependencies on such -a multiplatform library may fail to resolve because it isn't clear which target to choose. - -To solve this, mark the targets on both the library author and consumer sides with a custom attribute, which Gradle uses -during dependency resolution. - -For example, consider a testing library that supports both JUnit and TestNG in the two targets. The library author needs -to add an attribute to both targets as follows: - - - - -```kotlin -val testFrameworkAttribute = Attribute.of("com.example.testFramework", String::class.java) - -kotlin { - jvm("junit") { - attributes.attribute(testFrameworkAttribute, "junit") - } - jvm("testng") { - attributes.attribute(testFrameworkAttribute, "testng") - } -} -``` - - - - -```groovy -def testFrameworkAttribute = Attribute.of('com.example.testFramework', String) - -kotlin { - jvm('junit') { - attributes.attribute(testFrameworkAttribute, 'junit') - } - jvm('testng') { - attributes.attribute(testFrameworkAttribute, 'testng') - } -} -``` - - - - -The consumer has to add the attribute to a single target where the ambiguity arises. \ No newline at end of file diff --git a/docs/topics/native/native-target-support.md b/docs/topics/native/native-target-support.md index c46e7137ad5..4f32385b002 100644 --- a/docs/topics/native/native-target-support.md +++ b/docs/topics/native/native-target-support.md @@ -10,7 +10,7 @@ tiers depending on how well the compiler supports them. Mind the following terms used in tier tables: -* **Gradle target name** is a [target preset](multiplatform-set-up-targets.md) that is used in the +* **Gradle target name** is a [target name](multiplatform-dsl-reference.md#targets) that is used in the Kotlin Multiplatform Gradle plugin to enable the target. * **Target triple** is a target name according to the `---` structure that is commonly used by [compilers](https://clang.llvm.org/docs/CrossCompilation.html#target-triple). diff --git a/docs/topics/whatsnew1720.md b/docs/topics/whatsnew1720.md index 1aec3ec24b8..5a13c88e428 100644 --- a/docs/topics/whatsnew1720.md +++ b/docs/topics/whatsnew1720.md @@ -552,7 +552,7 @@ There are, however, some potentially breaking changes that may need your attenti * `org.jetbrains.kotlin.gradle.dsl.SingleTargetExtension` now has a generic parameter, `SingleTargetExtension`. * The `kotlin.targets.fromPreset()` convention has been deprecated. Instead, you can still use `kotlin.targets { fromPreset() }`, - but we recommend using more [specialized ways to create targets](multiplatform-set-up-targets.md). + but we recommend [setting up targets explicitly](multiplatform-discover-project.md#targets). * Target accessors auto-generated by Gradle are no longer available inside the `kotlin.targets { }` block. Please use the `findByName("targetName")` method instead. diff --git a/redirects/pages.yml b/redirects/pages.yml index ca2d4c4e902..98c56fea6f5 100644 --- a/redirects/pages.yml +++ b/redirects/pages.yml @@ -271,9 +271,6 @@ - from: /docs/reference/whatsnew12.html to: /docs/whatsnew12.html -- from: /docs/reference/mpp-set-up-targets.html - to: /docs/multiplatform-set-up-targets.html - - from: /docs/reference/equality.html to: /docs/equality.html @@ -564,9 +561,6 @@ - from: /docs/reference/visibility-modifiers.html to: /docs/visibility-modifiers.html -- from: /docs/reference/mpp-discover-project.html - to: /docs/multiplatform-discover-project.html - - from: /docs/reference/null-safety.html to: /docs/null-safety.html @@ -755,6 +749,9 @@ - /docs/multiplatform-mobile-understand-project-structure.html - /docs/kmm-understand-project-structure.html - /docs/mpp-discover-project.html + - /docs/reference/mpp-discover-project.html + - /docs/reference/mpp-set-up-targets.html + - /docs/multiplatform-set-up-targets.html to: /docs/multiplatform-discover-project.html - from: From 70964bd052bec7de6a84a2df859a38da80017987 Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Thu, 21 Nov 2024 13:15:13 +0100 Subject: [PATCH 17/32] feat: extra warnings compiler option (#4544) --- docs/topics/compiler-reference.md | 5 +++++ docs/topics/gradle/gradle-compiler-options.md | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/topics/compiler-reference.md b/docs/topics/compiler-reference.md index 03001fd6470..3245e984b6a 100644 --- a/docs/topics/compiler-reference.md +++ b/docs/topics/compiler-reference.md @@ -54,6 +54,11 @@ Suppress the compiler from displaying warnings during compilation. Turn any warnings into a compilation error. +### -Wextra + +Enable [additional declaration, expression, and type compiler checks](whatsnew21.md#extra-compiler-checks) that +emit warnings if true. + ### -verbose Enable verbose logging output which includes details of the compilation process. diff --git a/docs/topics/gradle/gradle-compiler-options.md b/docs/topics/gradle/gradle-compiler-options.md index d27401a9929..b9c2cf5a302 100644 --- a/docs/topics/gradle/gradle-compiler-options.md +++ b/docs/topics/gradle/gradle-compiler-options.md @@ -161,10 +161,11 @@ Here is a complete list of options for Gradle tasks: ### Common attributes -| Name | Description | Possible values | Default value | -|-------------------|------------------------------------------------------------------------------------------|---------------------------|---------------| -| `optIn` | A property for configuring a list of [opt-in compiler arguments](opt-in-requirements.md) | `listOf( /* opt-ins */ )` | `emptyList()` | -| `progressiveMode` | Enables the [progressive compiler mode](whatsnew13.md#progressive-mode) | `true`, `false` | `false` | +| Name | Description | Possible values | Default value | +|-------------------|------------------------------------------------------------------------------------------------------------------------------------------|---------------------------|---------------| +| `optIn` | A property for configuring a list of [opt-in compiler arguments](opt-in-requirements.md) | `listOf( /* opt-ins */ )` | `emptyList()` | +| `progressiveMode` | Enables the [progressive compiler mode](whatsnew13.md#progressive-mode) | `true`, `false` | `false` | +| `extraWarnings` | Enables [additional declaration, expression, and type compiler checks](whatsnew21.md#extra-compiler-checks) that emit warnings if true | `true`, `false` | `false` | ### Attributes specific to JVM From 76b2b25bd285701b8a026bcc55709c83c4ce82c4 Mon Sep 17 00:00:00 2001 From: alejandrapedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:16:48 +0100 Subject: [PATCH 18/32] feat: Wasm web APIs library (#4565) * update: Wasm web APIs library * chore: adding dependencies name * chore: fixing version in code snippet * chore: minor rephrase * update: moved section * chore: fix typo * Sarah's review --- docs/topics/wasm/wasm-js-interop.md | 30 +++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/docs/topics/wasm/wasm-js-interop.md b/docs/topics/wasm/wasm-js-interop.md index 168aae43d95..eb9968477ef 100644 --- a/docs/topics/wasm/wasm-js-interop.md +++ b/docs/topics/wasm/wasm-js-interop.md @@ -296,17 +296,12 @@ You can use nullable versions of these types as well. JavaScript values are represented in Kotlin using the `JsAny` type and its subtypes. -The standard library provides representation for some of these types: +The Kotlin/Wasm standard library provides representation for some of these types: * Package `kotlin.js`: * `JsAny` * `JsBoolean`, `JsNumber`, `JsString` * `JsArray` * `Promise` -* Package `org.khronos.webgl`: - * Typed arrays, like `Int8Array` - * WebGL types -* Packages `org.w3c.dom.*`: - * DOM API types You can also create custom `JsAny` subtypes by declaring an `external` interface or class. @@ -456,3 +451,26 @@ Although Kotlin/Wasm interoperability shares similarities with Kotlin/JS interop > ``` > {style="note"} + +## Web-related browser APIs + +The [`kotlinx-browser` library](https://github.com/kotlin/kotlinx-browser) is a standalone +library that provides JavaScript browser APIs, including: +* Package `org.khronos.webgl`: + * Typed arrays, like `Int8Array`. + * WebGL types. +* Packages `org.w3c.dom.*`: + * DOM API types. +* Package `kotlinx.browser`: + * DOM API global objects, like `window` and `document`. + +To use the declarations from the `kotlinx-browser` library, add it as a dependency in your +project's build configuration file: + +```kotlin +val wasmJsMain by getting { + dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-browser:0.3") + } +} +``` \ No newline at end of file From 47324570bc5cc42bad87b47f0e2cee74c03289dc Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Fri, 22 Nov 2024 15:26:33 +0100 Subject: [PATCH 19/32] update: deprecating memory allocator (#4527) --- docs/topics/native/native-binary-licenses.md | 1 - docs/topics/native/native-memory-manager.md | 27 +++++++------------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/docs/topics/native/native-binary-licenses.md b/docs/topics/native/native-binary-licenses.md index 21b28b83774..a31efef5b17 100644 --- a/docs/topics/native/native-binary-licenses.md +++ b/docs/topics/native/native-binary-licenses.md @@ -52,7 +52,6 @@ Always include the following license files for the corresponding projects: diff --git a/docs/topics/native/native-memory-manager.md b/docs/topics/native/native-memory-manager.md index 4983f7da346..a712fa14c91 100644 --- a/docs/topics/native/native-memory-manager.md +++ b/docs/topics/native/native-memory-manager.md @@ -81,11 +81,11 @@ kotlin.native.binary.gc=cms ### Disable garbage collection It's recommended to keep the GC enabled. However, you can disable it in certain cases, such as for testing purposes or -if you encounter issues and have a short-lived program. To do so, set the following compiler option in your -Gradle build script: +if you encounter issues and have a short-lived program. To do so, set the following binary option in your +`gradle.properties` file: ```none --Xgc=noop +kotlin.native.binary.gc=noop ``` > With this option enabled, the GC doesn't collect Kotlin objects, so memory consumption will keep rising as long as the @@ -155,23 +155,14 @@ If there are no memory leaks in the program, but you still see unexpectedly high try updating Kotlin to the latest version. We're constantly improving the memory manager, so even a simple compiler update might improve memory consumption. -If you continue to experience high memory consumption after updating, several options are available: +If you continue to experience high memory consumption after updating, switch to the system memory allocator by using +the following compiler option in your Gradle build script: -* Switch to a different memory allocator by using one of the following compiler options in your Gradle build script: - - * `-Xallocator=std` for the system allocator. - * `-Xallocator=mimalloc` for the [mimalloc](https://github.com/microsoft/mimalloc) allocator. - -* If you use the mimalloc allocator, you can instruct it to promptly release memory back to the system. - To do so, enable the following binary option in your `gradle.properties` file: - - ```none - kotlin.native.binary.mimallocUseCompaction=true - ``` - - It's a smaller performance cost, but it yields less certain results than the standard system allocator does. +```none +-Xallocator=std +``` -If none of these options improves your memory consumption, report an issue in [YouTrack](https://youtrack.jetbrains.com/newissue?project=kt). +If this doesn't improve your memory consumption, report an issue in [YouTrack](https://youtrack.jetbrains.com/newissue?project=kt). ## Unit tests in the background From 9fff1e9b812a2536fd02b254a52cbe24a7a8257f Mon Sep 17 00:00:00 2001 From: alejandrapedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:19:12 +0100 Subject: [PATCH 20/32] feat: Add information about array interoperability (#4554) * Add information about array interoperability * Slava review * TW review * chore: rephrase * chore: replace convert with copy --- docs/topics/wasm/wasm-js-interop.md | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/topics/wasm/wasm-js-interop.md b/docs/topics/wasm/wasm-js-interop.md index eb9968477ef..e9e6691b099 100644 --- a/docs/topics/wasm/wasm-js-interop.md +++ b/docs/topics/wasm/wasm-js-interop.md @@ -221,6 +221,47 @@ external class User : JsAny { } ``` +### Array interoperability + +You can copy JavaScript's `JsArray` into Kotlin's native `Array` or `List` types; likewise, +you can copy these Kotlin types to `JsArray`. + +To convert `JsArray` to `Array` or the other way around, use one of the available [adapter functions](https://github.com/Kotlin/kotlinx-browser/blob/dfbdceed314567983c98f1d66e8c2e10d99c5a55/src/wasmJsMain/kotlin/arrayCopy.kt). + +Here's an example of conversion between generic types: + +```kotlin +val list: List = + listOf("Kotlin", "Wasm").map { it.toJsString() } + +// Uses .toJsArray() to convert List or Array to JsArray +val jsArray: JsArray = list.toJsArray() + +// Uses .toArray() and .toList() to convert it back to Kotlin types +val kotlinArray: Array = jsArray.toArray() +val kotlinList: List = jsArray.toList() +``` + +Similar adapter functions are available for converting typed arrays to their Kotlin equivalents +(for example, `IntArray` and `Int32Array`). For detailed information and implementation, +see the [`kotlinx-browser` repository]( https://github.com/Kotlin/kotlinx-browser/blob/dfbdceed314567983c98f1d66e8c2e10d99c5a55/src/wasmJsMain/kotlin/arrayCopy.kt). + +Here's an example of conversion between typed arrays: + +```kotlin +import org.khronos.webgl.* + + // ... + + val intArray: IntArray = intArrayOf(1, 2, 3) + + // Uses .toInt32Array() to convert Kotlin IntArray to JavaScript Int32Array + val jsInt32Array: Int32Array = intArray.toInt32Array() + + // Uses toIntArray() to convert JavaScript Int32Array back to Kotlin IntArray + val kotlnIntArray: IntArray = jsInt32Array.toIntArray() +``` + ## Use Kotlin code in JavaScript Learn how to use your Kotlin code in JavaScript by using the `@JsExport` annotation. From 8f7d27c78f9f73d3932da91aa8771ac300535c5b Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Fri, 22 Nov 2024 17:51:12 +0100 Subject: [PATCH 21/32] update: deprecating android target (#4536) --- .../multiplatform/multiplatform-compatibility-guide.md | 10 +++++++--- .../multiplatform-configure-compilations.md | 6 +++--- .../multiplatform/multiplatform-discover-project.md | 2 +- .../multiplatform/multiplatform-dsl-reference.md | 10 +++++----- docs/topics/multiplatform/multiplatform-publish-lib.md | 2 +- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/topics/multiplatform/multiplatform-compatibility-guide.md b/docs/topics/multiplatform/multiplatform-compatibility-guide.md index dc47d7fc4d4..e542a4681aa 100644 --- a/docs/topics/multiplatform/multiplatform-compatibility-guide.md +++ b/docs/topics/multiplatform/multiplatform-compatibility-guide.md @@ -620,18 +620,22 @@ support for the Android target. In the future, this support will be provided via Android team from Google. To open the way for the new solution from Google, we're renaming the `android` block to `androidTarget` in the current -Kotlin DSL in 1.9.0. This is a temporary change that is necessary to free the short `android` name for the upcoming DSL +Kotlin DSL. This is a temporary change that is necessary to free the short `android` name for the upcoming DSL from Google. **What's the best practice now?** -Rename all the occurrences of the `android` block to `androidTarget`. When the new plugin for the Android target support +Rename all the occurrences of the `android` block to `androidTarget`. When the new plugin for the Android target support is available, migrate to the DSL from Google. It will be the preferred option to work with Android in Kotlin Multiplatform projects. **When do the changes take effect?** -In Kotlin 1.9.0, a deprecation warning is introduced when the `android` name is used in Kotlin Multiplatform projects. +Here's the planned deprecation cycle: + +* 1.9.0: introduce a deprecation warning when the `android` name is used in Kotlin Multiplatform projects +* 2.1.0: raise this warning to an error +* 2.2.0: remove the `android` target DSL from the Kotlin Multiplatform Gradle plugin ## Declaring several similar targets diff --git a/docs/topics/multiplatform/multiplatform-configure-compilations.md b/docs/topics/multiplatform/multiplatform-configure-compilations.md index d03f3a84aaa..844649ec915 100644 --- a/docs/topics/multiplatform/multiplatform-configure-compilations.md +++ b/docs/topics/multiplatform/multiplatform-configure-compilations.md @@ -329,8 +329,8 @@ for each build variant, a Kotlin compilation is created under the same name. Then, for each [Android source set](https://developer.android.com/studio/build/build-variants#sourcesets) compiled for each of the variants, a Kotlin source set is created under that source set name prepended by the target name, like the -Kotlin source set `androidDebug` for an Android source set `debug` and the Kotlin target named `android`. These Kotlin -source sets are added to the variants' compilations accordingly. +Kotlin source set `androidDebug` for an Android source set `debug` and the Kotlin target named `androidTarget`. +These Kotlin source sets are added to the variants' compilations accordingly. The default source set `commonMain` is added to each production (application or library) variant's compilation. The `commonTest` source set is similarly added to the compilations of unit test and instrumented test variants. @@ -341,7 +341,7 @@ than within Kotlin source set dependencies. ```kotlin kotlin { - android { /* ... */ } + androidTarget { /* ... */ } } dependencies { diff --git a/docs/topics/multiplatform/multiplatform-discover-project.md b/docs/topics/multiplatform/multiplatform-discover-project.md index 632cbc86862..7d1ca746bd8 100644 --- a/docs/topics/multiplatform/multiplatform-discover-project.md +++ b/docs/topics/multiplatform/multiplatform-discover-project.md @@ -204,7 +204,7 @@ Consider an example where you need to target all modern Apple devices and Androi ```kotlin kotlin { - android() + androidTarget() iosArm64() // 64-bit iPhone devices macosArm64() // Modern Apple Silicon-based Macs watchosX64() // Modern 64-bit Apple Watch devices diff --git a/docs/topics/multiplatform/multiplatform-dsl-reference.md b/docs/topics/multiplatform/multiplatform-dsl-reference.md index a5d824c30b9..c37ce3e6960 100644 --- a/docs/topics/multiplatform/multiplatform-dsl-reference.md +++ b/docs/topics/multiplatform/multiplatform-dsl-reference.md @@ -52,7 +52,7 @@ that specific target. Learn more about [setting up targets](multiplatform-discov Each target can have one or more [compilations](#compilations). In addition to default compilations for test and production purposes, you can [create custom compilations](multiplatform-configure-compilations.md#create-a-custom-compilation). -The targets of a multiplatform project are described in the corresponding blocks inside `kotlin {}`, for example, `jvm`, `android`, `iosArm64`. +The targets of a multiplatform project are described in the corresponding blocks inside `kotlin {}`, for example, `jvm`, `androidTarget`, `iosArm64`. The complete list of available targets is the following:
    Target platformTarget presetTarget Comments

    MIT license

    Include in case you use the mimaloc memory allocator instead of the default one (the -Xallocator=mimalloc compiler option is set).

    -

    For more information on allocators, see Kotlin/Native memory management

    @@ -96,7 +96,7 @@ The complete list of available targets is the following: - + + + + +
    Android applications and librariesandroidandroidTarget

    Manually apply an Android Gradle plugin: com.android.application or com.android.library.

    You can only create one Android target per Gradle subproject.

    @@ -452,15 +452,15 @@ Two functions help you configure [build variants](https://developer.android.com/ ```kotlin kotlin { - android { - publishLibraryVariants("release", "debug") + androidTarget { + publishLibraryVariants("release") } } ``` Learn more about [compilation for Android](multiplatform-configure-compilations.md#compilation-for-android). -> The `android` configuration inside `kotlin` doesn't replace the build configuration of any Android project. +> The `androidTarget` configuration inside the `kotlin {}` block doesn't replace the build configuration of any Android project. > Learn more about writing build scripts for Android projects in [Android developer documentation](https://developer.android.com/studio/build). > {style="note"} diff --git a/docs/topics/multiplatform/multiplatform-publish-lib.md b/docs/topics/multiplatform/multiplatform-publish-lib.md index 6e9b1854580..e64d9455297 100644 --- a/docs/topics/multiplatform/multiplatform-publish-lib.md +++ b/docs/topics/multiplatform/multiplatform-publish-lib.md @@ -130,7 +130,7 @@ specify the variant names in the Android target block in the `shared/build.gradl ```kotlin kotlin { androidTarget { - publishLibraryVariants("release", "debug") + publishLibraryVariants("release") } } From 70a9dbd436e16e6a35817b7c43b840b1235c1126 Mon Sep 17 00:00:00 2001 From: alejandrapedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Mon, 25 Nov 2024 14:41:56 +0100 Subject: [PATCH 22/32] feat: Wasm: improve debug ux (#4534) * wasm: improved debugging experience doc update & updated wizard image * Danil review --- docs/images/wasm/wasm-compose-wizard.png | Bin 126989 -> 0 bytes docs/topics/wasm/wasm-debugging.md | 54 ++++++++++++++++++++--- docs/topics/wasm/wasm-get-started.md | 11 ++--- 3 files changed, 53 insertions(+), 12 deletions(-) delete mode 100644 docs/images/wasm/wasm-compose-wizard.png diff --git a/docs/images/wasm/wasm-compose-wizard.png b/docs/images/wasm/wasm-compose-wizard.png deleted file mode 100644 index 8dd81cec0f6e0105333a668dc727012903fc2703..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 126989 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 z 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 z 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 diff --git a/docs/topics/wasm/wasm-debugging.md b/docs/topics/wasm/wasm-debugging.md index 6d4f82075bb..aefb1d7ad4c 100644 --- a/docs/topics/wasm/wasm-debugging.md +++ b/docs/topics/wasm/wasm-debugging.md @@ -21,7 +21,7 @@ Create a project using the Kotlin Multiplatform wizard: 3. Select the **Web** option. Make sure that no other options are selected. 4. Click the **Download** button and unpack the resulting archive. -![Kotlin Multiplatform wizard](wasm-compose-web-wizard.png){width=600} +![Kotlin Multiplatform wizard](wasm-compose-web-wizard.png){width=400} ## Open the project in IntelliJ IDEA @@ -39,7 +39,7 @@ Create a project using the Kotlin Multiplatform wizard: 2. In **composeApp** | **Tasks** | **kotlin browser**, select and run the **wasmJsBrowserRun** task. - ![Run the Gradle task](wasm-gradle-task-window.png){width=600} + ![Run the Gradle task](wasm-gradle-task-window.png){width=550} Alternatively, you can run the following command in the terminal from the `WasmDemo` root directory: @@ -60,11 +60,11 @@ Create a project using the Kotlin Multiplatform wizard: You see a "Click me!" button. Click it: - ![Click me](wasm-composeapp-browser-clickme.png){width=650} + ![Click me](wasm-composeapp-browser-clickme.png){width=550} Now you see the Compose Multiplatform logo: - ![Compose app in browser](wasm-composeapp-browser.png){width=650} + ![Compose app in browser](wasm-composeapp-browser.png){width=550} ## Debug in your browser @@ -82,6 +82,8 @@ build file. For more information about how to configure your browser for debuggi ### Configure your browser for debugging {initial-collapse-state="collapsed" collapsible="true"} +#### Enable access to project's sources + By default, browsers can't access some of the project's sources necessary for debugging. To provide access, you can configure the Webpack DevServer to serve these sources. In the `ComposeApp` directory, add the following code snippets to your `build.gradle.kts` file. @@ -132,6 +134,42 @@ kotlin { > {style="note"} +#### Enhance your debugging with custom formatters + +In addition to the default debugging configuration, you can set up custom formatters to display and locate variable values in a more user-friendly and comprehensible manner. + +![Kotlin/Wasm improved debugger](wasm-debugger-improved.png){width=600} + +This implementation is supported across major browsers like Firefox and Chromium-based as +it uses the [custom formatters API](https://firefox-source-docs.mozilla.org/devtools-user/custom_formatters/index.html). + +To set up custom formatters for an improved debugging experience: + +1. Add the following compiler argument to the `wasmJs` compiler options: + + ```kotlin + kotlin { + wasmJs { + // ... + compilerOptions { + freeCompilerArgs.add("-Xwasm-debugger-custom-formatters") + } + } + } + ``` + +2. Enable the **Custom formatters** feature in your browser: + + * In the Chrome DevTools, it's placed in **Settings | Preferences | Console**: + + ![Enable custom formatters in Chrome](wasm-custom-formatters-chrome.png){width=400} + + * In the Firefox Developer Tools, it's placed in **Settings | Advanced settings**: + + ![Enable custom formatters in Firefox](wasm-custom-formatters-firefox.png){width=400} + +After setting up custom formatters, you can complete the debugging tutorial. + ### Debug your Kotlin/Wasm application > This tutorial uses the Chrome browser, but you should be able to follow these steps with other browsers. For more information, @@ -147,7 +185,7 @@ kotlin { 3. Click on the line numbers to set breakpoints on the code that you want to inspect. Only the lines with darker numbers can have breakpoints. -![Set breakpoints](wasm-breakpoints.png){width=700} + ![Set breakpoints](wasm-breakpoints.png){width=600} 4. Click on the **Click me!** button to interact with the application. This action triggers the execution of the code, and the debugger pauses when the execution reaches a breakpoint. @@ -157,11 +195,13 @@ kotlin { * ![Step over](wasm-step-over.png){width=30}{type="joined"} Step over to execute the current line and pause on the next line. * ![Step out](wasm-step-out.png){width=30}{type="joined"} Step out to execute the code until it exits the current function. -![Debug controls](wasm-debug-controls.png){width=700} + ![Debug controls](wasm-debug-controls.png){width=600} 6. Check the **Call stack** and **Scope** panes to trace the sequence of function calls and pinpoint the location of any errors. -![Check call stack](wasm-debug-scope.png){width=700} + ![Check call stack](wasm-debug-scope.png){width=550} + + For an improved visualization of the variable values, see [Enhance your debugging with custom formatters](#enhance-your-debugging-with-custom-formatters). 7. Make changes to your code and [run the application](#run-the-application) again to verify that everything works as expected. 8. Click on the line numbers with breakpoints to remove the breakpoints. diff --git a/docs/topics/wasm/wasm-get-started.md b/docs/topics/wasm/wasm-get-started.md index e4bdee65c94..854aa2ba1c7 100644 --- a/docs/topics/wasm/wasm-get-started.md +++ b/docs/topics/wasm/wasm-get-started.md @@ -23,7 +23,7 @@ Create a project using the Kotlin Multiplatform wizard: 3. Select the **Web** option. Make sure that no other options are selected. 4. Click the **Download** button and unpack the resulting archive. -![Kotlin Multiplatform wizard](wasm-compose-web-wizard.png){width=600} +![Kotlin Multiplatform wizard](wasm-compose-web-wizard.png){width=400} ## Open the project in IntelliJ IDEA @@ -110,7 +110,8 @@ Join the Kotlin/Wasm community in Kotlin Slack: Try more Kotlin/Wasm examples: -* [Compose image viewer](https://github.com/JetBrains/compose-multiplatform/tree/master/examples/imageviewer) -* [Jetsnack application](https://github.com/JetBrains/compose-multiplatform/tree/master/examples/jetsnack) -* [Node.js example](https://github.com/Kotlin/kotlin-wasm-nodejs-template) -* [WASI example](https://github.com/Kotlin/kotlin-wasm-wasi-template) +* [Compose image viewer](https://github.com/Kotlin/kotlin-wasm-examples/tree/main/compose-imageviewer) +* [Jetsnack application](https://github.com/Kotlin/kotlin-wasm-examples/tree/main/compose-jetsnack) +* [Node.js example](https://github.com/Kotlin/kotlin-wasm-examples/tree/main/nodejs-example) +* [WASI example](https://github.com/Kotlin/kotlin-wasm-examples/tree/main/wasi-example) +* [Compose example](https://github.com/Kotlin/kotlin-wasm-examples/tree/main/compose-example) \ No newline at end of file From 0f7381b71f0c06b6b3d2ec334f7b8305614b837c Mon Sep 17 00:00:00 2001 From: alejandrapedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:09:24 +0100 Subject: [PATCH 23/32] feat: Add incremental compilation section in Wasm Troubleshooting (#4553) * Add incremental compilation section in Wasm Troubleshooting * chore: Zalim review Co-authored-by: Zalim Bashorov * adding information about slow compilation --------- Co-authored-by: Zalim Bashorov --- docs/topics/wasm/wasm-troubleshooting.md | 25 +++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/topics/wasm/wasm-troubleshooting.md b/docs/topics/wasm/wasm-troubleshooting.md index a89e5a35fc2..e863f85aed6 100644 --- a/docs/topics/wasm/wasm-troubleshooting.md +++ b/docs/topics/wasm/wasm-troubleshooting.md @@ -108,4 +108,27 @@ export { moduleExports as default }; You can place your new `.mjs` file in the resources folder, and it will automatically be placed next to the main `.mjs` file during the build process. You can also place your `.mjs` file in a custom location. In this case, you need to either manually move it next to the main `.mjs` file or -adjust the path in the import statement to match its location. \ No newline at end of file +adjust the path in the import statement to match its location. + +## Slow Kotlin/Wasm compilation + +When working on Kotlin/Wasm projects, you may experience slow compilation times. This happens because the Kotlin/Wasm +toolchain recompiles the entire codebase every time you make a change. + +To mitigate this issue, Kotlin/Wasm targets support incremental compilation, which enables the compiler to recompile only +those files relevant to changes from the last compilation. + +Using incremental compilation reduces the compilation time. It doubles +the development speed for now, with plans to improve it further in future releases. + +In the current setup, incremental compilation for the Wasm targets is disabled by default. +To enable it, add the following line to your project's `local.properties` or `gradle.properties` file: + +```text +kotlin.incremental.wasm=true +``` + +> Try out the Kotlin/Wasm incremental compilation and [share your feedback](https://youtrack.jetbrains.com/issue/KT-72158/Kotlin-Wasm-incremental-compilation-feedback). +> Your insights help make the feature stable and enabled by default sooner. +> +{style="note"} \ No newline at end of file From 1b44cc139608b31b7bbeee6d51baf4de5b4e25f6 Mon Sep 17 00:00:00 2001 From: Andrey Polyakov Date: Tue, 26 Nov 2024 10:24:17 +0100 Subject: [PATCH 24/32] update: update the v.list and clean the EAP builds info (#4571) * update: update v.list and clean up the EAP info * fix: small wording fixes --- data/releases.yml | 4 ++-- docs/kr.tree | 7 ++++--- docs/topics/eap.md | 4 +++- docs/topics/home.topic | 2 +- docs/topics/install-eap-plugin.md | 5 +++-- docs/topics/releases.md | 10 ++++++++++ docs/topics/whatsnew21.md | 23 +++++++++++++++++++++++ docs/v.list | 18 +++++++++--------- 8 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 docs/topics/whatsnew21.md diff --git a/data/releases.yml b/data/releases.yml index 83538613e2a..90b10633d9f 100644 --- a/data/releases.yml +++ b/data/releases.yml @@ -1,5 +1,5 @@ url: https://github.com/JetBrains/kotlin/releases latest: - version: 2.0.21 - url: https://github.com/JetBrains/kotlin/releases/tag/v2.0.21 \ No newline at end of file + version: 2.1.0 + url: https://github.com/JetBrains/kotlin/releases/tag/v2.1.0 \ No newline at end of file diff --git a/docs/kr.tree b/docs/kr.tree index 0668c704c23..f4f123d79fd 100644 --- a/docs/kr.tree +++ b/docs/kr.tree @@ -23,10 +23,11 @@ - - - + + What's New in Kotlin 2.0.20 + What's new in Kotlin 2.1.0 Kotlin releases Standard library API reference Basic types diff --git a/docs/topics/install-eap-plugin.md b/docs/topics/install-eap-plugin.md index ecae6ac9136..7231a35d4da 100644 --- a/docs/topics/install-eap-plugin.md +++ b/docs/topics/install-eap-plugin.md @@ -1,8 +1,9 @@ [//]: # (title: Install the EAP Plugin for IntelliJ IDEA or Android Studio) -

    Latest Kotlin EAP release: %kotlinEapVersion%

    -

    Explore Kotlin EAP release details

    +

    No preview versions are currently available

    +
    > Starting from IntelliJ IDEA 2023.3 and Android Studio Iguana (2023.2.1) Canary 15, the Kotlin plugin doesn't need to be diff --git a/docs/topics/releases.md b/docs/topics/releases.md index 962cf527442..9427dc7864b 100644 --- a/docs/topics/releases.md +++ b/docs/topics/releases.md @@ -99,6 +99,16 @@ The following table lists details of the latest Kotlin releases:
    Build info Build highlights
    2.1.0 +

    Released: November 27, 2024

    +

    Release on GitHub

    +
    +

    A language release introducing new language features.

    +

    Learn more about Kotlin 2.1.0 in What's new in Kotlin 2.1.0.

    +
    2.0.21

    Released: October 10, 2024

    diff --git a/docs/topics/whatsnew21.md b/docs/topics/whatsnew21.md new file mode 100644 index 00000000000..3b307f2c003 --- /dev/null +++ b/docs/topics/whatsnew21.md @@ -0,0 +1,23 @@ +[//]: # (title: What's new in Kotlin 2.1.0) + +_[Released: November 27, 2024](releases.md#release-details)_ + +## IDE support + +The Kotlin plugins that support 2.1.0 are bundled in the latest IntelliJ IDEA and Android Studio. +You don't need to update the Kotlin plugin in your IDE. +All you need to do is [change the Kotlin version](configure-build-for-eap.md) to 2.1.0 in your build scripts. + +See [Update to a new Kotlin version](releases.md#update-to-a-new-kotlin-version) for details. + +## Documentation updates + +The Kotlin documentation has received some notable changes: + +## Install Kotlin 2.1.0 + +Starting from IntelliJ IDEA 2023.3 and Android Studio Iguana (2023.2.1) Canary 15, the Kotlin plugin is distributed as a +bundled plugin included in your IDE. This means that you can't install the plugin from JetBrains Marketplace anymore. + +To update to the new Kotlin version, [change the Kotlin version](releases.md#update-to-a-new-kotlin-version) +to 2.1.0 in your build scripts. \ No newline at end of file diff --git a/docs/v.list b/docs/v.list index c1db0170a1d..3c9885acd78 100644 --- a/docs/v.list +++ b/docs/v.list @@ -5,12 +5,12 @@ - - - + + + - - + + @@ -26,7 +26,7 @@ - + @@ -38,12 +38,12 @@ - - + + - + From 95f19adcc86d44a31b83fe41265cfe7aa763a621 Mon Sep 17 00:00:00 2001 From: alejandrapedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:21:22 +0100 Subject: [PATCH 25/32] feat: update gradle-compiler-options.md and fix anchors (#4531) * update gradle-compiler-options.md and fix anchors * Yahor review * Yahor review round 2 * chore: fix code snippets * chore: rephrase DSL levels explanation * Aleksey review * Added explanation about Android tasks * Explanation about Kotlin Gradle plugin extensions * chore: rephrased note * chore: add anchor --- .../gradle/gradle-compilation-and-caches.md | 2 +- docs/topics/gradle/gradle-compiler-options.md | 189 +++++++++++++----- docs/topics/k2-compiler-migration-guide.md | 2 +- docs/topics/whatsnew1920.md | 2 +- 4 files changed, 141 insertions(+), 54 deletions(-) diff --git a/docs/topics/gradle/gradle-compilation-and-caches.md b/docs/topics/gradle/gradle-compilation-and-caches.md index afcf5dd0cf0..0d79833c121 100644 --- a/docs/topics/gradle/gradle-compilation-and-caches.md +++ b/docs/topics/gradle/gradle-compilation-and-caches.md @@ -317,7 +317,7 @@ From Kotlin 2.0.0, the K2 compiler is used by default. To use the previous compiler from Kotlin 2.0.0 onwards, either: -* In your `build.gradle.kts` file, [set your language version](gradle-compiler-options.md#example-of-setting-a-languageversion) to `1.9`. +* In your `build.gradle.kts` file, [set your language version](gradle-compiler-options.md#example-of-setting-languageversion) to `1.9`. OR * Use the following compiler option: `-language-version 1.9`. diff --git a/docs/topics/gradle/gradle-compiler-options.md b/docs/topics/gradle/gradle-compiler-options.md index b9c2cf5a302..4935a9c7973 100644 --- a/docs/topics/gradle/gradle-compiler-options.md +++ b/docs/topics/gradle/gradle-compiler-options.md @@ -13,53 +13,111 @@ in the [Working with command-line compiler](command-line.md) tutorial. ## How to define options -Kotlin compilers have a number of options for tailoring the compiling process. +Kotlin compilers have a number of options for tailoring the compiling process. -Using a build script, you can specify additional compilation options. Use the `compilerOptions` property of a Kotlin compilation task for it. -For example: +The Gradle DSL allows comprehensive +configuration of compiler options. It is available for [Kotlin Multiplatform](multiplatform-dsl-reference.md) and [JVM/Android](#target-the-jvm) projects. - - +With the Gradle DSL, you can configure compiler options within the build script at three levels: +* **Extension level**, in the `kotlin {}` block for all targets and shared source sets. +* **Target level**, in the block for a specific target. +* **Compilation unit level,** usually in a specific compilation task. + +![Kotlin compiler options levels](compiler-options-levels.svg){width=700} + +The settings at a higher level are used as a convention (default) for a lower level: + +* Compiler options set at the extension level are the default for target-level options, including shared source sets + like `commonMain`, `nativeMain`, and `commonTest`. +* Compiler options set at the target level are the default for options at the compilation unit (task) level, + like `compileKotlinJvm` and `compileTestKotlinJvm` tasks. + +In turn, configurations made at a lower level override related settings at a higher level: + +* Task-level compiler options override related configurations at the target or the extension level. +* Target-level compiler options override related configurations at the extension level. + +To find out which level of compiler arguments is applied to the compilation, use the `DEBUG` level of Gradle [logging](https://docs.gradle.org/current/userguide/logging.html). +For JVM and JS/WASM tasks, search for the `"Kotlin compiler args:"` string within the logs; for Native tasks, +search for the `"Arguments ="` string. + +> If you're a third-party plugin author, it's best to apply your configuration on the project level to avoid +> overriding issues. You can use the new [Kotlin plugin DSL extension types](whatsnew21.md#new-api-for-kotlin-gradle-plugin-extensions) for this. It's recommended that you document this +> configuration on your side explicitly. +> +{style="tip"} + +### Extension level + +You can configure common compiler options for all the targets and shared source sets +in the `compilerOptions {}` block at the top level: ```kotlin -tasks.named("compileKotlin", org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask::class.java) { +kotlin { compilerOptions { - freeCompilerArgs.add("-Xexport-kdoc") + optIn.add("kotlin.RequiresOptIn") } -} +} ``` - - +### Target level -```groovy -tasks.named('compileKotlin', org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask.class) { - compilerOptions { - freeCompilerArgs.add("-Xexport-kdoc") +You can configure compiler options for the JVM/Android target +in the `compilerOptions {}` block inside the `target {}` block: + +```kotlin +kotlin { + target { + compilerOptions { + optIn.add("kotlin.RequiresOptIn") + } } } ``` - - +In Kotlin Multiplatform projects, you can configure compiler options inside the +specific target. For example, `jvm { compilerOptions {}}`. For more information, see [Multiplatform Gradle DSL reference](multiplatform-dsl-reference.md). -### Target the JVM +### Compilation unit level -JVM compilation tasks are called `compileKotlin` for production code and `compileTestKotlin` -for test code. The tasks for custom source sets are named according to their `compileKotlin` patterns. +You can configure compiler options for a specific compilation unit or task in a `compilerOptions {}` +block inside the task configuration: -The names of the tasks in Android Projects contain [build variant](https://developer.android.com/studio/build/build-variants.html) -names and follow the `compileKotlin` pattern, for example, `compileDebugKotlin` or `compileReleaseUnitTestKotlin`. +```Kotlin +tasks.named("compileKotlin"){ + compilerOptions { + optIn.add("kotlin.RequiresOptIn") + } +} +``` -For both the JVM and Android projects, it's possible to define options using the project Kotlin extension DSL: +You can also access and configure compiler options at a compilation unit level via `KotlinCompilation`: + +```Kotlin +kotlin { + target { + val main by compilations.getting { + compileTaskProvider.configure { + compilerOptions { + + } + } + } + } +} +``` + +If you want to configure a plugin of a target different from JVM/Android and [Kotlin Multiplatform](multiplatform-dsl-reference.md), +use the `compilerOptions {}` property of the corresponding Kotlin compilation task. The following examples +show how to set this configuration up in both Kotlin and Groovy DSLs: ```kotlin -kotlin { +tasks.named("compileKotlin", org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask::class.java) { compilerOptions { - apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.%gradleApiVersion%) + apiVersion.set("1.8") } } ``` @@ -68,9 +126,9 @@ kotlin { ```groovy -kotlin { +tasks.named('compileKotlin', org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask.class) { compilerOptions { - apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.%gradleApiVersion% + apiVersion.set("1.8") } } ``` @@ -78,6 +136,16 @@ kotlin { +## Target the JVM + +As explained before, you can define compiler options for your JVM/Android projects at the extension, target, and compilation unit levels. + +Default JVM compilation tasks are called `compileKotlin` for production code and `compileTestKotlin` +for test code. The tasks for custom source sets are named according to their `compileKotlin` patterns. + +You can see the list of Android compilation tasks by running the `gradlew tasks --all` command in the terminal +and searching for `compile*Kotlin` task names in the `Other tasks` group. + Some important details to be aware of: * The `android.kotlinOptions` and `kotlin.compilerOptions` configuration blocks override each other. The last (lowest) block takes effect. @@ -85,7 +153,7 @@ Some important details to be aware of: * You can override the configuration applied by `kotlin.compilerOptions` DSL using the `tasks.named("compileKotlin") { }` (or `tasks.withType().configureEach { }`) approach. -### Target JavaScript +## Target JavaScript JavaScript compilation tasks are called `compileKotlinJs` for production code, `compileTestKotlinJs` for test code, and `compileKotlinJs` for custom source sets. @@ -124,9 +192,9 @@ Note that with the Gradle Kotlin DSL, you should get the task from the project's Use the `Kotlin2JsCompile` and `KotlinCompileCommon` types for JS and common targets, respectively. -### For all Kotlin compilation tasks +## All Kotlin compilation tasks -It is also possible to configure all of the Kotlin compilation tasks in the project: +It is also possible to configure all the Kotlin compilation tasks in the project: @@ -157,7 +225,7 @@ tasks.named('compileKotlin', KotlinCompilationTask) { ## All compiler options -Here is a complete list of options for Gradle tasks: +Here is a complete list of options for the Gradle compiler: ### Common attributes @@ -176,7 +244,7 @@ Here is a complete list of options for Gradle tasks: | `noJdk` | Don't automatically include the Java runtime into the classpath | | false | | `jvmTargetValidationMode` |
  • Validation of the [JVM target compatibility](gradle-configure-project.md#check-for-jvm-target-compatibility-of-related-compile-tasks) between Kotlin and Java
  • A property for tasks of the `KotlinCompile` type.
  • | `WARNING`, `ERROR`, `INFO` | `ERROR` | -### Attributes common to JVM and JS +### Attributes common to JVM and JavaScript | Name | Description | Possible values |Default value | |------|-------------|----------------------------------------------------------------|--------------| @@ -187,7 +255,6 @@ Here is a complete list of options for Gradle tasks: | `apiVersion` | Restrict the use of declarations to those from the specified version of bundled libraries | "1.8", "1.9", "2.0", "2.1", "2.2" (EXPERIMENTAL) | | | `languageVersion` | Provide source compatibility with the specified version of Kotlin | "1.8", "1.9", "2.0", "2.1", "2.2" (EXPERIMENTAL) | | - > We are going to deprecate the attribute `freeCompilerArgs` in future releases. If you miss some option in the Kotlin Gradle DSL, > please, [file an issue](https://youtrack.jetbrains.com/newissue?project=kt). > @@ -195,8 +262,8 @@ Here is a complete list of options for Gradle tasks: #### Example of additional arguments usage via freeCompilerArgs {initial-collapse-state="collapsed" collapsible="true"} -Use the attribute `freeCompilerArgs` to supply additional (including experimental) compiler arguments. You can add a single -argument to this attribute or a list of arguments: +Use the `freeCompilerArgs` attribute to supply additional (including experimental) compiler arguments. +You can add a single argument to this attribute or a list of arguments: @@ -205,14 +272,27 @@ argument to this attribute or a list of arguments: import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask // ... -val compileKotlin: KotlinCompilationTask<*> by tasks +kotlin { + compilerOptions { + // Specifies the version of the Kotlin API and the JVM target + apiVersion.set(KotlinVersion.%gradleLanguageVersion%) + jvmTarget.set(JvmTarget.JVM_1_8) + + // Single experimental argument + freeCompilerArgs.add("-Xexport-kdoc") + + // Single additional argument + freeCompilerArgs.add("-Xno-param-assertions") -// Single experimental argument -compileKotlin.compilerOptions.freeCompilerArgs.add("-Xexport-kdoc") -// Single additional argument, can be a key-value pair -compileKotlin.compilerOptions.freeCompilerArgs.add("-Xno-param-assertions") -// List of arguments -compileKotlin.compilerOptions.freeCompilerArgs.addAll(listOf("-Xno-receiver-assertions", "-Xno-call-assertions")) + // List of arguments + freeCompilerArgs.addAll( + listOf( + "-Xno-receiver-assertions", + "-Xno-call-assertions" + ) + ) + } +} ``` @@ -224,10 +304,16 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask tasks.named('compileKotlin', KotlinCompilationTask) { compilerOptions { + // Specifies the version of the Kotlin API and the JVM target + apiVersion = KotlinVersion.%gradleLanguageVersion% + jvmTarget = JvmTarget.JVM_1_8 + // Single experimental argument freeCompilerArgs.add("-Xexport-kdoc") + // Single additional argument, can be a key-value pair freeCompilerArgs.add("-Xno-param-assertions") + // List of arguments freeCompilerArgs.addAll(["-Xno-receiver-assertions", "-Xno-call-assertions"]) } @@ -237,7 +323,11 @@ tasks.named('compileKotlin', KotlinCompilationTask) {
    -#### Example of setting a languageVersion +> The `freeCompilerArgs` attribute is available at the [extension](#extension-level), [target](#target-level), and [compilation unit (task)](#compilation-unit-level) levels. +> +{style="tip"} + +#### Example of setting languageVersion {initial-collapse-state="collapsed" collapsible="true"} To set a language version, use the following syntax: @@ -245,15 +335,11 @@ To set a language version, use the following syntax: ```kotlin -tasks - .withType() - .configureEach { - compilerOptions - .languageVersion - .set( - org.jetbrains.kotlin.gradle.dsl.KotlinVersion.%gradleLanguageVersion% - ) +kotlin { + compilerOptions { + languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.%gradleLanguageVersion%) } +} ``` @@ -273,7 +359,7 @@ tasks Also, see [Types for compiler options](#types-for-compiler-options). -### Attributes specific to JS +### Attributes specific to JavaScript | Name | Description | Possible values |Default value | |---|---|---|---| @@ -305,6 +391,7 @@ Some of the `compilerOptions` use the new types instead of the `String` type: ## What's next? Learn more about: +* [Kotlin Multiplatform DSL reference](multiplatform-dsl-reference.md). * [Incremental compilation, caches support, build reports, and the Kotlin daemon](gradle-compilation-and-caches.md). * [Gradle basics and specifics](https://docs.gradle.org/current/userguide/userguide.html). * [Support for Gradle plugin variants](gradle-plugin-variants.md). diff --git a/docs/topics/k2-compiler-migration-guide.md b/docs/topics/k2-compiler-migration-guide.md index 15e68bbcdd4..6f07dc46447 100644 --- a/docs/topics/k2-compiler-migration-guide.md +++ b/docs/topics/k2-compiler-migration-guide.md @@ -598,7 +598,7 @@ The Kotlin Playground supports Kotlin 2.0.0 and later releases. [Check it out!]( To use the previous compiler in Kotlin 2.0.0 and later releases, either: -* In your `build.gradle.kts` file, [set your language version](gradle-compiler-options.md#example-of-setting-a-languageversion) to `1.9`. +* In your `build.gradle.kts` file, [set your language version](gradle-compiler-options.md#example-of-setting-languageversion) to `1.9`. OR * Use the following compiler option: `-language-version 1.9`. diff --git a/docs/topics/whatsnew1920.md b/docs/topics/whatsnew1920.md index 538326efdb1..7e8cd26bbc1 100644 --- a/docs/topics/whatsnew1920.md +++ b/docs/topics/whatsnew1920.md @@ -59,7 +59,7 @@ kapt.use.k2=true ``` Alternatively, you can enable K2 for kapt by completing the following steps: -1. In your `build.gradle.kts` file, [set the language version](gradle-compiler-options.md#example-of-setting-a-languageversion) to `2.0`. +1. In your `build.gradle.kts` file, [set the language version](gradle-compiler-options.md#example-of-setting-languageversion) to `2.0`. 2. In your `gradle.properties` file, add `kapt.use.k2=true`. If you encounter any issues when using kapt with the K2 compiler, please report them to our From fa262421c66a080ef9cd274669b47c125f4f9d48 Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Tue, 26 Nov 2024 13:36:40 +0100 Subject: [PATCH 26/32] update: host requirements (#4549) --- .../multiplatform-publish-lib.md | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/topics/multiplatform/multiplatform-publish-lib.md b/docs/topics/multiplatform/multiplatform-publish-lib.md index e64d9455297..b1c792e96da 100644 --- a/docs/topics/multiplatform/multiplatform-publish-lib.md +++ b/docs/topics/multiplatform/multiplatform-publish-lib.md @@ -49,13 +49,23 @@ in the publication's scope. ## Host requirements -Except for Apple platform targets, Kotlin/Native supports cross-compilation, allowing any host to produce needed artifacts. +Kotlin/Native supports cross-compilation, allowing any host to produce necessary `.klib` artifacts. -To avoid any issues during publication: -* Publish only from an Apple host when your project targets Apple operating systems. -* Publish all artifacts from one host only to avoid duplicating publications in the repository. - - Maven Central, for example, explicitly forbids duplicate publications and fails the process. +To produce artifacts for projects with Apple targets, you'd normally need an Apple machine. +However, if you want to use other hosts, you can set this [Experimental](components-stability.md#stability-levels-explained) +option in your `gradle.properties` file: + +```none +kotlin.native.enableKlibsCrossCompilation=true +``` + +> To build [final binaries](multiplatform-build-native-binaries.md) for Apple targets, you still need to use a Mac machine. +> +{style="note"} + +To avoid any issues during publication, publish all artifacts from a single host to avoid duplicating publications in the +repository. Maven Central, for example, explicitly forbids duplicate publications and fails the process. + ### If you use Kotlin 1.7.0 or earlier {initial-collapse-state="collapsed" collapsible="true"} From 8ac5fd8f162208f79643368f529ebfb06ff9e9eb Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Tue, 26 Nov 2024 14:08:31 +0100 Subject: [PATCH 27/32] feat: shortcut deprecation section (#4537) --- .../multiplatform-compatibility-guide.md | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/docs/topics/multiplatform/multiplatform-compatibility-guide.md b/docs/topics/multiplatform/multiplatform-compatibility-guide.md index e542a4681aa..2f6cd353c49 100644 --- a/docs/topics/multiplatform/multiplatform-compatibility-guide.md +++ b/docs/topics/multiplatform/multiplatform-compatibility-guide.md @@ -421,7 +421,8 @@ The access to `KotlinCompilation.source` has been deprecated. A code like this p kotlin { jvm() js() - ios() + iosArm64() + iosSimulatorArm64() sourceSets { val commonMain by getting @@ -447,7 +448,8 @@ You can change the code above in one of the following ways: kotlin { jvm() js() - ios() + iosArm64() + iosSimulatorArm64() sourceSets { val commonMain by getting @@ -915,6 +917,41 @@ Here's the planned deprecation cycle: with "unresolved reference" errors, and binaries (for example, Gradle plugins) might fail with linkage errors unless recompiled against the latest versions of the Kotlin Gradle plugin + +## Deprecated Apple target shortcuts + +**What's changed?** + +We're deprecating `ios()`, `watchos()`, and `tvos()` target shortcuts in Kotlin Multiplatform DSL. They were designed to +partially create a source set hierarchy for Apple targets. However, they proved to be difficult to expand and sometimes confusing. + +For example, the `ios()` shortcut created both the `iosArm64` and `iosX64` targets but didn't include the `iosSimulatorArm64` +target, which is necessary when working on hosts with Apple M chips. However, changing this shortcut was hard to implement +and could cause issues in existing user projects. + +**What's the best practice now?** + +The Kotlin Gradle plugin now provides a built-in hierarchy template. Since Kotlin 1.9.20, it's enabled by default +and contains predefined intermediate source sets for popular use cases. + +Instead of shortcuts, you should specify the list of targets, and then the plugin automatically sets up intermediate +source sets based on this list. + +For example, if you have `iosArm64` and `iosSimulatorArm64` targets in your project, the plugin automatically creates +the `iosMain` and `iosTest` intermediate source sets. If you have `iosArm64` and `macosArm64` targets, the `appleMain` and +`appleTest` source sets are created. + +For more information, see [Hierarchical project structure](multiplatform-hierarchy.md) + +**When do the changes take effect?** + +Here's the planned deprecation cycle: + +* 1.9.20: report a warning when `ios()`, `watchos()`, and `tvos()` target shortcuts are used; + the default hierarchy template is enabled by default instead +* 2.1.0: report an error when target shortcuts are used +* 2.2.0: remove target shortcut DSL from the Kotlin Multiplatform Gradle plugin + ## New approach to forward declarations **What's changed?** From fd3a501e223e0166995258869e272411fd107c80 Mon Sep 17 00:00:00 2001 From: Aleksey Zamulla Date: Tue, 26 Nov 2024 14:32:05 +0100 Subject: [PATCH 28/32] feature: PausableComposition feature flag for the Compose compiler (#4573) --- docs/topics/compose-compiler-options.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/topics/compose-compiler-options.md b/docs/topics/compose-compiler-options.md index 936b043adb2..814b7075d4f 100644 --- a/docs/topics/compose-compiler-options.md +++ b/docs/topics/compose-compiler-options.md @@ -181,6 +181,10 @@ This results in fewer slots being used and fewer comparisons being made at runti ### OptimizeNonSkippingGroups +> This feature is considered [Experimental](components-stability.md#stability-levels-explained). +> +{style="warning"} + **Default**: disabled If enabled, remove groups around non-skipping composable functions. @@ -190,10 +194,27 @@ unnecessary groups around composables which do not skip (and thus do not require This optimization will remove the groups, for example, around functions explicitly marked as `@NonSkippableComposable` and functions that are implicitly not skippable (inline functions and functions that return a non-`Unit` value such as `remember`). -> This feature is considered [Experimental](components-stability.md#stability-levels-explained) and is disabled by default. +### PausableComposition + +> This feature is considered [Experimental](components-stability.md#stability-levels-explained). > {style="warning"} +**Default**: disabled + +If enabled, changes the code generation of composable functions to allow pausing when part of a pausable composition. +This lets Compose runtime suspend composition at skipping points, +splitting long-running compositions across multiple frames. + +Lazy lists and other performance intensive components use pausable composition to prefetch content +that might cause visual jank when executed in a blocking manner. + +> The feature flag affects behavior only with a version of Compose runtime that supports pausable composition, +> starting with `androidx.compose.runtime` 1.8.0-alpha02. +> Older versions ignore the feature flag. +> +{style="note"} + ### StrongSkipping **Default**: enabled From a2ef18ce5f21416b9afd62bfffbe77956b24e70f Mon Sep 17 00:00:00 2001 From: Andrey Polyakov Date: Tue, 26 Nov 2024 15:52:44 +0100 Subject: [PATCH 29/32] feat: add multi-dollar string interpolation feature (#4569) * feat: add multi-dollar string interpolation feature * update: rewrite the section after review * fix: fixes after review * fix: remove strange new line * fix: add non-strange new line * fix: remove strange full stop --- docs/topics/strings.md | 101 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 6 deletions(-) diff --git a/docs/topics/strings.md b/docs/topics/strings.md index b05570f194d..c176be01a1b 100644 --- a/docs/topics/strings.md +++ b/docs/topics/strings.md @@ -86,13 +86,14 @@ See [Characters](characters.md) page for the list of supported escape sequences. ### Multiline strings -_Multiline strings_ can contain newlines and arbitrary text. It is delimited by a triple quote (`"""`), contains no escaping and can contain newlines and any other characters: +_Multiline strings_ can contain newlines and arbitrary text. It is delimited by a triple quote (`"""`), +contains no escaping and can contain newlines and any other characters: ```kotlin val text = """ for (c in "foo") print(c) -""" + """ ``` To remove leading whitespace from multiline strings, use the [`trimMargin()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/trim-margin.html) function: @@ -143,9 +144,9 @@ fun main() { ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} -You can use templates both in multiline and escaped strings. -To insert the dollar sign `$` in a multiline string (which doesn't support backslash escaping) before any symbol, -which is allowed as a beginning of an [identifier](https://kotlinlang.org/docs/reference/grammar.html#identifiers), +You can use templates both in multiline and escaped strings. However, multiline strings don't support backslash escaping. +To insert the dollar sign `$` in a multiline string +before any symbol allowed at the beginning of an [identifier](https://kotlinlang.org/docs/reference/grammar.html#identifiers), use the following syntax: ```kotlin @@ -154,6 +155,94 @@ ${'$'}_9.99 """ ``` +> To avoid `${'$'}` sequences in strings, you can use the Experimental [multi-dollar string interpolation feature](#multi-dollar-string-interpolation). +> +{style="note"} + +### Multi-dollar string interpolation + +> The multi-dollar string interpolation is [Experimental](https://kotlinlang.org/docs/components-stability.html#stability-levels-explained) +> and opt-in is required (see details below). +> +> It may be changed at any time. We would appreciate your feedback in [YouTrack](https://youtrack.jetbrains.com/issue/KT-2425). +> +{style="warning"} + +Multi-dollar string interpolation allows you to specify how many consecutive dollar signs are required to trigger interpolation. +Interpolation is the process of embedding variables or expressions directly into a string. + +While you can [escape literals](#escaped-strings) for single-line strings, +multiline strings in Kotlin don't support backslash escaping. +To include dollar signs (`$`) as literal characters, you must use the `${'$'}` construct to prevent string interpolation. +This approach can make code harder to read, especially when strings contain multiple dollar signs. + +Multi-dollar string interpolation simplifies this +by letting you treat dollar signs as literal characters in both single-line and multiline strings. +For example: + +```kotlin +val KClass<*>.jsonSchema : String + get() = $$""" + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.com/product.schema.json", + "$dynamicAnchor": "meta" + "title": "$${simpleName ?: qualifiedName ?: "unknown"}", + "type": "object" + } + """ +``` + +Here, the `$$` prefix specifies that two consecutive dollar signs are required to trigger string interpolation. +Single dollar signs remain as literal characters. + +You can adjust how many dollar signs trigger interpolation. +For example, using three consecutive dollar signs (`$$$`) allows `$` and `$$` to remain as literals +while enabling interpolation with `$$$`: + +```kotlin +val productName = "carrot" +val requestedData = + $$$"""{ + "currency": "$", + "enteredAmount": "42.45 $$", + "$$serviceField": "none", + "product": "$$$productName" + } + """ + +println(requestedData) +//{ +// "currency": "$", +// "enteredAmount": "42.45 $$", +// "$$serviceField": "none", +// "product": "carrot" +//} +``` + +Here, the `$$$` prefix allows the string to include `$` and `$$` without requiring the `${'$'}` construct for escaping. + +To enable the feature, use the following compiler option in the command line: + +```bash +kotlinc -Xmulti-dollar-interpolation main.kt +``` + +Alternatively, update the `compilerOptions {}` block of your Gradle build file: + +```kotlin +// build.gradle.kts +kotlin { + compilerOptions { + freeCompilerArgs.add("-Xmulti-dollar-interpolation") + } +} +``` + +This feature doesn't affect existing code that uses single-dollar string interpolation. +You can continue using a single `$` +as before and apply multi-dollar signs when you need to handle literal dollar signs in strings. + ## String formatting > String formatting with the `String.format()` function is only available in Kotlin/JVM. @@ -210,4 +299,4 @@ In addition, you can assign the format string from a variable. This can be usefu for example, in localization cases that depend on the user locale. Be careful when using the `String.format()` function because it can be easy to mismatch the number or position of the -arguments with their corresponding placeholders. +arguments with their corresponding placeholders. \ No newline at end of file From 4fcaa750474445b3209f2d2387fd5b341e08e5ed Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Wed, 27 Nov 2024 11:48:11 +0100 Subject: [PATCH 30/32] feat: non-local jumps (#4566) --- docs/topics/inline-functions.md | 29 ++++++++++++++++++++++--- docs/topics/keyword-reference.md | 2 +- docs/topics/lambdas.md | 2 +- docs/topics/returns.md | 37 ++++++++++++-------------------- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/docs/topics/inline-functions.md b/docs/topics/inline-functions.md index b252b6d582d..78bd9d10112 100644 --- a/docs/topics/inline-functions.md +++ b/docs/topics/inline-functions.md @@ -54,7 +54,9 @@ however, can be manipulated in any way you like, including being stored in field > {style="note"} -## Non-local returns +## Non-local jump expressions + +### Returns In Kotlin, you can only use a normal, unqualified `return` to exit a named function or an anonymous function. To exit a lambda, use a [label](returns.md#return-to-labels). A bare `return` is forbidden @@ -122,9 +124,30 @@ inline fun f(crossinline body: () -> Unit) { } ``` -> `break` and `continue` are not yet available in inlined lambdas, but we are planning to support them, too. +### Break and continue + +> This feature is currently [In preview](kotlin-evolution-principles.md#pre-stable-features). +> We're planning to stabilize it in future releases. +> To opt in, use the `-Xnon-local-break-continue` compiler option. +> We would appreciate your feedback on it in [YouTrack](https://youtrack.jetbrains.com/issue/KT-1436). > -{style="note"} +{style="warning"} + +Similar to non-local `return`, you can apply `break` and `continue` [jump expressions](returns.md) in lambdas passed +as arguments to an inline function that encloses a loop: + +```kotlin +fun processList(elements: List): Boolean { + for (element in elements) { + val variable = element.nullableMethod() ?: run { + log.warning("Element is null or invalid, continuing...") + continue + } + if (variable == 0) return true + } + return false +} +``` ## Reified type parameters diff --git a/docs/topics/keyword-reference.md b/docs/topics/keyword-reference.md index aaf0f265a56..9343f7de16c 100644 --- a/docs/topics/keyword-reference.md +++ b/docs/topics/keyword-reference.md @@ -94,7 +94,7 @@ in other contexts: * `annotation` declares an [annotation class](annotations.md). * `companion` declares a [companion object](object-declarations.md#companion-objects). * `const` marks a property as a [compile-time constant](properties.md#compile-time-constants). - * `crossinline` forbids [non-local returns in a lambda passed to an inline function](inline-functions.md#non-local-returns). + * `crossinline` forbids [non-local returns in a lambda passed to an inline function](inline-functions.md#returns). * `data` instructs the compiler to [generate canonical members for a class](data-classes.md). * `enum` declares an [enumeration](enum-classes.md). * `expect` marks a declaration as [platform-specific](multiplatform-expect-actual.md), expecting an implementation in platform modules. diff --git a/docs/topics/lambdas.md b/docs/topics/lambdas.md index f04f08cb12f..7a428faa5f4 100644 --- a/docs/topics/lambdas.md +++ b/docs/topics/lambdas.md @@ -330,7 +330,7 @@ functions with a block body. > {style="note"} -Another difference between lambda expressions and anonymous functions is the behavior of [non-local returns](inline-functions.md#non-local-returns). +Another difference between lambda expressions and anonymous functions is the behavior of [non-local returns](inline-functions.md#returns). A `return` statement without a label always returns from the function declared with the `fun` keyword. This means that a `return` inside a lambda expression will return from the enclosing function, whereas a `return` inside an anonymous function will return from the anonymous function itself. diff --git a/docs/topics/returns.md b/docs/topics/returns.md index 6e1948b5025..bdd6eee1b61 100644 --- a/docs/topics/returns.md +++ b/docs/topics/returns.md @@ -26,7 +26,7 @@ loop@ for (i in 1..100) { } ``` -Now, we can qualify a `break` or a `continue` with a label: +Now, you can qualify a `break` or a `continue` with a label: ```kotlin loop@ for (i in 1..100) { @@ -39,32 +39,18 @@ loop@ for (i in 1..100) { A `break` qualified with a label jumps to the execution point right after the loop marked with that label. A `continue` proceeds to the next iteration of that loop. +> In some cases, you can apply `break` and `continue` *non-locally* without explicitly defining labels. +> Such non-local usages are valid in lambda expressions used in enclosing [inline functions](inline-functions.md#break-and-continue). +> +{style="note"} + ## Return to labels In Kotlin, functions can be nested using function literals, local functions, and object expressions. -Qualified `return`s allow us to return from an outer function. -The most important use case is returning from a lambda expression. Recall that when we write the following, -the `return`-expression returns from the nearest enclosing function - `foo`: +A qualified `return` allows you to return from an outer function. -```kotlin -//sampleStart -fun foo() { - listOf(1, 2, 3, 4, 5).forEach { - if (it == 3) return // non-local return directly to the caller of foo() - print(it) - } - println("this point is unreachable") -} -//sampleEnd - -fun main() { - foo() -} -``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -Note that such non-local returns are supported only for lambda expressions passed to [inline functions](inline-functions.md). -To return from a lambda expression, label it and qualify the `return`: +The most important use case is returning from a lambda expression. To return from a lambda expression, +label it and qualify the `return`: ```kotlin //sampleStart @@ -153,3 +139,8 @@ return@a 1 ``` This means "return `1` at label `@a`" rather than "return a labeled expression `(@a 1)`". + +> In some cases, you can return from a lambda expression without using labels. Such *non-local* returns are located in a +> lambda but exit the enclosing [inline function](inline-functions.md#returns). +> +{style="note"} From 1cfc1bc160e9801a96d6e69a9bd60596871c554c Mon Sep 17 00:00:00 2001 From: Andrey Polyakov Date: Wed, 27 Nov 2024 16:18:37 +0100 Subject: [PATCH 31/32] feat: add What's new in Kotlin 2.1.0 (#4574) * feat: Guard conditions in when expressions (#4417) * Guard conditions in when expressions This PR adds information about Guard conditions in when expressions and how to use them. * Alejandro's review * chore: adding `is` in a branch Co-authored-by: Alejandro Serrano * chore: adding `data` in code line Co-authored-by: Alejandro Serrano * Andrey review * Update docs/topics/control-flow.md Co-authored-by: Andrey Polyakov * chore: fix code comments --------- Co-authored-by: Alejandro Serrano Co-authored-by: Andrey Polyakov * feat: add What's new in Kotlin 2.1.0 * update: update WN in Kotlin 2.1.0 * fix: rollback style changed * fix: style fixes * update: some doc updates * fix: apply patch * Revert "feat: Guard conditions in when expressions (#4417)" This reverts commit b778f28c3b3ff63ccea45a9e1637648e49f745e3. * fix: fix the link * fix: deliver fixes * fix: deliver fixes after review * fix: deliver fixes after review --------- Co-authored-by: alejandrapedroza <39709865+AlejandraPedroza@users.noreply.github.com> Co-authored-by: Alejandro Serrano --- docs/topics/wasm/wasm-js-interop.md | 6 +- docs/topics/whatsnew21.md | 1565 +++++++++++++++++++++++++++ 2 files changed, 1568 insertions(+), 3 deletions(-) diff --git a/docs/topics/wasm/wasm-js-interop.md b/docs/topics/wasm/wasm-js-interop.md index e9e6691b099..401c723da85 100644 --- a/docs/topics/wasm/wasm-js-interop.md +++ b/docs/topics/wasm/wasm-js-interop.md @@ -399,7 +399,7 @@ You can use Kotlin `try-catch` expression to catch JavaScript exceptions. However, accessing specific details about the thrown value in Kotlin/Wasm isn’t possible by default. You can configure the `JsException` type to include the original error message and stack trace from JavaScript. -To do so, add the following compiler flag to your `build.gradle.kts` file: +To do so, add the following compiler option to your `build.gradle.kts` file: ```kotlin kotlin { @@ -443,8 +443,8 @@ fun main() { } ``` -With the `-Xwasm-attach-js-exception` flag enabled, the `JsException` type provides specific details from the JavaScript error. -Without the flag, `JsException` includes only a generic message stating that an exception was thrown while running JavaScript code. +With the `-Xwasm-attach-js-exception` compiler option enabled, the `JsException` type provides specific details from the JavaScript error. +Without enabling this compiler option, `JsException` includes only a generic message stating that an exception was thrown while running JavaScript code. If you try to use a JavaScript `try-catch` expression to catch Kotlin/Wasm exceptions, it looks like a generic `WebAssembly.Exception` without directly accessible messages and data. diff --git a/docs/topics/whatsnew21.md b/docs/topics/whatsnew21.md index 3b307f2c003..28bad751f51 100644 --- a/docs/topics/whatsnew21.md +++ b/docs/topics/whatsnew21.md @@ -2,6 +2,19 @@ _[Released: November 27, 2024](releases.md#release-details)_ +The Kotlin 2.1.0 release is here! Here are the main highlights: + +* **New language features in preview**: [Guard conditions in `when` with a subject](#guard-conditions-in-when-with-a-subject), + [non-local `break` and `continue`](#non-local-break-and-continue), and [multi-dollar string interpolation](#multi-dollar-string-interpolation). +* **K2 compiler updates**: [More flexibility around compiler checks](#extra-compiler-checks) and [improvements to the kapt implementation](#improved-k2-kapt-implementation). +* **Kotlin Multiplatform**: Introduced [basic support for Swift export](#basic-support-for-swift-export), + [stable Gradle DSL for compiler options](#new-gradle-dsl-for-compiler-options-in-multiplatform-projects-promoted-to-stable), and more. +* **Kotlin/Native**: [Improved support for `iosArm64`](#iosarm64-promoted-to-tier-1) and other updates. +* **Kotlin/Wasm**: Multiple updates, including [support for incremental compilation](#support-for-incremental-compilation). +* **Gradle support**: [Improved compatibility with newer versions of Gradle and the Android Gradle plugin](#gradle-improvements), + along with [updates to the Kotlin Gradle plugin API](#new-api-for-kotlin-gradle-plugin-extensions). +* **Documentation**: [Significant improvements to the Kotlin documentation](#documentation-updates). + ## IDE support The Kotlin plugins that support 2.1.0 are bundled in the latest IntelliJ IDEA and Android Studio. @@ -10,10 +23,1562 @@ All you need to do is [change the Kotlin version](configure-build-for-eap.md) to See [Update to a new Kotlin version](releases.md#update-to-a-new-kotlin-version) for details. +## Language + +After the Kotlin 2.0.0 release with the K2 compiler, the JetBrains team is focusing on improving the language with new features. +In this release, we are excited to announce several new language design improvements. + +These features are available in preview, and we encourage you to try them and share your feedback: + +* [Guard conditions in `when` with a subject](#guard-conditions-in-when-with-a-subject) +* [Non-local `break` and `continue`](#non-local-break-and-continue) +* [Multi-dollar interpolation: improved handling of `$` in string literals](#multi-dollar-string-interpolation) + +> All the features have IDE support in the latest 2024.3 version of IntelliJ IDEA with K2 mode enabled. +> +> Learn more in the [IntelliJ IDEA 2024.3 blog post](https://blog.jetbrains.com/idea/2024/11/intellij-idea-2024-3/). +> +{style="tip"} + +[See the full list of Kotlin language design features and proposals](kotlin-language-features-and-proposals.md). + +This release also brings the following language updates: + +* [](#support-for-requiring-opt-in-to-extend-apis) +* [](#improved-overload-resolution-for-functions-with-generic-types) +* [](#improved-exhaustiveness-checks-for-when-expressions-with-sealed-classes) + +### Guard conditions in when with a subject + +> This feature is [In preview](kotlin-evolution-principles.md#pre-stable-features), +> and opt-in is required (see details below). +> +> We would appreciate your feedback in [YouTrack](https://youtrack.jetbrains.com/issue/KT-71140). +> +{style="warning"} + +Starting from 2.1.0, you can use guard conditions in `when` expressions or statements with subjects. + +Guard conditions allow you to include more than one condition for the branches of a `when` expression, +making complex control flows more explicit and concise as well as flattening the code structure. + +To include a guard condition in a branch, place it after the primary condition, separated by `if`: + +```kotlin +sealed interface Animal { + data class Cat(val mouseHunter: Boolean) : Animal { + fun feedCat() {} + } + + data class Dog(val breed: String) : Animal { + fun feedDog() {} + } +} + +fun feedAnimal(animal: Animal) { + when (animal) { + // Branch with only the primary condition. Returns `feedDog()` when `Animal` is `Dog` + is Animal.Dog -> animal.feedDog() + // Branch with both primary and guard conditions. Returns `feedCat()` when `Animal` is `Cat` and is not `mouseHunter` + is Animal.Cat if !animal.mouseHunter -> animal.feedCat() + // Returns "Unknown animal" if none of the above conditions match + else -> println("Unknown animal") + } +} +``` + +In a single `when` expression, you can combine branches with and without guard conditions. +The code in a branch with a guard condition runs only if both the primary condition and the guard condition are `true`. +If the primary condition does not match, the guard condition is not evaluated. +Additionally, guard conditions support `else if`. + +To enable guard conditions in your project, use the following compiler option in the command line: + +```bash +kotlinc -Xwhen-guards main.kt +``` + +Or add it to the `compilerOptions {}` block of your Gradle build file: + +```kotlin +// build.gradle.kts +kotlin { + compilerOptions { + freeCompilerArgs.add("-Xwhen-guards") + } +} +``` + +### Non-local break and continue + +> This feature is [In preview](kotlin-evolution-principles.md#pre-stable-features), +> and opt-in is required (see details below). +> +> We would appreciate your feedback in [YouTrack](https://youtrack.jetbrains.com/issue/KT-1436). +> +{style="warning"} + +Kotlin 2.1.0 adds a preview of another long-awaited feature, the ability to use non-local `break` and `continue`. +This feature expands the toolset you can use in the scope of inline functions and reduces boilerplate code in your project. + +Previously, you could use only non-local returns. +Now, Kotlin also supports `break` and `continue` [jump expressions](returns.md) non-locally. +This means that you can apply them within lambdas passed as arguments to an inline function that encloses the loop: + +```kotlin +fun processList(elements: List): Boolean { + for (element in elements) { + val variable = element.nullableMethod() ?: run { + log.warning("Element is null or invalid, continuing...") + continue + } + if (variable == 0) return true // If variable is zero, return true + } + return false +} +``` + +To try the feature in your project, use the `-Xnon-local-break-continue` compiler option in the command line: + +```bash +kotlinc -Xnon-local-break-continue main.kt +``` + +Or add it in the `compilerOptions {}` block of your Gradle build file: + +```kotlin +// build.gradle.kts +kotlin { + compilerOptions { + freeCompilerArgs.add("-Xnon-local-break-continue") + } +} +``` + +We're planning to make this feature Stable in future Kotlin releases. +If you encounter any issues when using non-local `break` and `continue`, +please report them to our [issue tracker](https://youtrack.jetbrains.com/issue/KT-1436). + +### Multi-dollar string interpolation + +> The feature is [In preview](kotlin-evolution-principles.md#pre-stable-features) +> and opt-in is required (see details below). +> +> We would appreciate your feedback in [YouTrack](https://youtrack.jetbrains.com/issue/KT-2425). +> +{style="warning"} + +Kotlin 2.1.0 introduces support for multi-dollar string interpolation, +improving how the dollar sign (`$`) is handled within string literals. +This feature is helpful in contexts that require multiple dollar signs, +such as templating engines, JSON schemas, or other data formats. + +String interpolation in Kotlin uses a single dollar sign. +However, using a literal dollar sign in a string, +which is common in financial data and templating systems, required workarounds such as `${'$'}`. +With the multi-dollar interpolation feature enabled, you can configure how many dollar signs trigger interpolation, +with fewer dollar signs being treated as string literals. + +Here's an example of how to generate an JSON schema multi-line string with placeholders using `$`: + +```kotlin +val KClass<*>.jsonSchema : String + get() = $$""" + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.com/product.schema.json", + "$dynamicAnchor": "meta" + "title": "$${simpleName ?: qualifiedName ?: "unknown"}", + "type": "object" + } + """ +``` + +In this example, the initial `$$` means that you need **two dollar signs** (`$$`) to trigger interpolation. +It prevents `$schema`, `$id`, and `$dynamicAnchor` from being interpreted as interpolation markers. + +This approach is especially helpful when working with systems that use dollar signs for placeholder syntax. + +To enable the feature, use the following compiler option in the command line: + +```bash +kotlinc -Xmulti-dollar-interpolation main.kt +``` + +Alternatively, update the `compilerOptions {}` block of your Gradle build file: + +```kotlin +// build.gradle.kts +kotlin { + compilerOptions { + freeCompilerArgs.add("-Xmulti-dollar-interpolation") + } +} +``` + +If your code already uses standard string interpolation with a single dollar sign, no changes are needed. +You can use `$$` whenever you need literal dollar signs in your strings. + +### Support for requiring opt-in to extend APIs + +Kotlin 2.1.0 introduces the [`@SubclassOptInRequired`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-subclass-opt-in-required/) annotation, +which allows library authors to require an explicit opt-in before users can implement experimental interfaces or extend experimental classes. + +This feature can be useful when a library API is stable enough to use but might evolve with new abstract functions, +making it unstable for inheritance. + +To add the opt-in requirement to an API element, use the `@SubclassOptInRequired` +annotation with a reference to the annotation class: + +```kotlin +@RequiresOptIn( +level = RequiresOptIn.Level.WARNING, +message = "Interfaces in this library are experimental" +) +annotation class UnstableApi() + +@SubclassOptInRequired(UnstableApi::class) +interface CoreLibraryApi +``` + +In this example, the `CoreLibraryApi` interface requires users to opt in before they can implement it. +A user can opt in like this: + +```kotlin +@OptIn(UnstableApi::class) +interface MyImplementation: CoreLibraryApi +``` + +> When you use the `@SubclassOptInRequired` annotation to require opt-in, +> the requirement is not propagated to any [inner or nested classes](nested-classes.md). +> +{style="note"} + +For a real-world example of how to use the `@SubclassOptInRequired` annotation in your API, +check out the [`SharedFlow`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-shared-flow/) +interface in the `kotlinx.coroutines` library. + +### Improved overload resolution for functions with generic types + +Previously, if you had a number of overloads for a function where some had value parameters of a generic type +and others had function types in the same position, the resolution behavior could sometimes be inconsistent. + +This led to different behavior depending on whether your overloads were member functions or extension functions. +For example: + +```kotlin +class KeyValueStore { + fun store(key: K, value: V) {} // 1 + fun store(key: K, lazyValue: () -> V) {} // 2 +} + +fun KeyValueStore.storeExtension(key: K, value: V) {} // 1 +fun KeyValueStore.storeExtension(key: K, lazyValue: () -> V) {} // 2 + +fun test(kvs: KeyValueStore) { + // Member functions + kvs.store("", 1) // Resolves to 1 + kvs.store("") { 1 } // Resolves to 2 + + // Extension functions + kvs.storeExtension("", 1) // Resolves to 1 + kvs.storeExtension("") { 1 } // Doesn't resolve +} +``` + +In this example, the `KeyValueStore` class has two overloads for the `store()` function, +where one overload has function parameters with generic types `K` and `V`, +and another has a lambda function that returns a generic type `V`. +Similarly, there are two overloads for the extension function: `storeExtension()`. + +When the `store()` function was called with and without a lambda function, +the compiler successfully resolved the correct overloads. +However, when the extension function `storeExtension()` was called with a lambda function, +the compiler didn't resolve the correct overload because it incorrectly considered both overloads to be applicable. + +To fix this problem, we've introduced a new heuristic so that the compiler can discard a possible overload +when a function parameter with a generic type can't accept a lambda function based on information from a different argument. +This change makes the behavior of member functions and extension functions consistent, +and it is enabled by default in Kotlin 2.1.0. + +### Improved exhaustiveness checks for when expressions with sealed classes + +In previous versions of Kotlin, the compiler required an `else` branch in `when` +expressions for type parameters with sealed upper bounds, even when all cases in the `sealed class` hierarchy were covered. +This behavior is addressed and improved in Kotlin 2.1.0, +making exhaustiveness checks more powerful and allowing you to remove redundant `else` branches, +keeping `when` expressions cleaner and more intuitive. + +Here's an example demonstrating the change: + +```kotlin +sealed class Result +object Error: Result() +class Success(val value: String): Result() + +fun render(result: T) = when (result) { + Error -> "Error!" + is Success -> result.value + // Requires no else branch +} +``` + +## Kotlin K2 compiler + +With Kotlin 2.1.0, the K2 compiler now provides [more flexibility when working with compiler checks](#extra-compiler-checks) +and [warnings](#global-warning-suppression), as well as [improved support for the kapt plugin](#improved-k2-kapt-implementation). + +### Extra compiler checks + +With Kotlin 2.1.0, you can now enable additional checks in the K2 compiler. +These are extra declaration, expression, and type checks that are usually not crucial for compilation +but can still be useful if you want to validate the following cases: + +| Check type | Comment | +|-------------------------------------------------------|------------------------------------------------------------------------------------------| +| `REDUNDANT_NULLABLE` | `Boolean??` is used instead of `Boolean?` | +| `PLATFORM_CLASS_MAPPED_TO_KOTLIN` | `java.lang.String` is used instead of `kotlin.String` | +| `ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS` | `arrayOf("") == arrayOf("")` is used instead of `arrayOf("").contentEquals(arrayOf(""))` | +| `REDUNDANT_CALL_OF_CONVERSION_METHOD` | `42.toInt()` is used instead of `42` | +| `USELESS_CALL_ON_NOT_NULL` | `"".orEmpty()` is used instead of `""` | +| `REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE` | `"$string"` is used instead of `string` | +| `UNUSED_ANONYMOUS_PARAMETER` | A parameter is passed in the lambda expression but never used | +| `REDUNDANT_VISIBILITY_MODIFIER` | `public class Klass` is used instead of `class Klass` | +| `REDUNDANT_MODALITY_MODIFIER` | `final class Klass` is used instead of `class Klass` | +| `REDUNDANT_SETTER_PARAMETER_TYPE` | `set(value: Int)` is used instead of `set(value)` | +| `CAN_BE_VAL` | `var local = 0` is defined but never reassigned, can be `val local = 42` instead | +| `ASSIGNED_VALUE_IS_NEVER_READ` | `val local = 42` is defined but never used afterward in the code | +| `UNUSED_VARIABLE` | `val local = 0` is defined but never used in the code | +| `REDUNDANT_RETURN_UNIT_TYPE` | `fun foo(): Unit {}` is used instead of `fun foo() {}` | +| `UNREACHABLE_CODE` | Code statement is present but can never be executed | + +If the check is true, you'll receive a compiler warning with a suggestion on how to fix the problem. + +Extra checks are disabled by default. +To enable them, use the `-Wextra` compiler option in the command line or specify `extraWarnings` +in the `compilerOptions {}` block of your Gradle build file: + +```kotlin +// build.gradle.kts +kotlin { + compilerOptions { + extraWarnings.set(true) + } +} +``` + +For more information on how to define and use compiler options, +see [Compiler options in the Kotlin Gradle plugin](gradle-compiler-options.md). + +### Global warning suppression + +In 2.1.0, the Kotlin compiler has received a highly requested feature – the ability to suppress warnings globally. + +You can now suppress specific warnings in the whole project by using the `-Xsuppress-warning=WARNING_NAME` +syntax in the command line or the `freeCompilerArgs` attribute in the `compilerOptions {}` block of your build file. + +For example, if you have [extra compiler checks](#extra-compiler-checks) enabled in your project but want to suppress one of them, use: + +```kotlin +// build.gradle.kts +kotlin { + compilerOptions { + extraWarnings.set(true) + freeCompilerArgs.add("-Xsuppress-warning=CAN_BE_VAL") + } +} +``` + +If you want to suppress a warning but don't know its name, select the element and click the light bulb icon (or use Cmd + Enter/Alt + Enter): + +![Warning name intention](warning-name-intention.png){width=500} + +The new compiler option is currently [Experimental](components-stability.md#stability-levels-explained). +The following details are also worth noting: + +* Error suppression is not allowed. +* If you pass an unknown warning name, compilation will result in an error. +* You can specify several warnings at once: + + + + + ```bash + kotlinc -Xsuppress-warning=NOTHING_TO_INLINE -Xsuppress-warning=NO_TAIL_CALLS_FOUND main.kt + ``` + + + + + ```kotlin + // build.gradle.kts + kotlin { + compilerOptions { + freeCompilerArgs.addAll( + listOf( + "-Xsuppress-warning=NOTHING_TO_INLINE", + "-Xsuppress-warning=NO_TAIL_CALLS_FOUND" + ) + ) + } + } + ``` + + + + +### Improved K2 kapt implementation + +> The kapt plugin for the K2 compiler (K2 kapt) is in [Alpha](components-stability.md#stability-levels-explained). +> It may be changed at any time. +> +> We would appreciate your feedback in [YouTrack](https://youtrack.jetbrains.com/issue/KT-71439/K2-kapt-feedback). +> +{style="warning"} + +Currently, projects using the [kapt](kapt.md) plugin work with the K1 compiler by default, +supporting Kotlin versions up to 1.9. + +In Kotlin 1.9.20, we launched an experimental implementation of the kapt plugin with the K2 compiler (K2 kapt). +We have now improved K2 kapt's internal implementation to mitigate technical and performance issues. + +While the new K2 kapt implementation doesn't introduce new features, +its performance has significantly improved compared to the previous K2 kapt implementation. +Additionally, the K2 kapt plugin's behavior is now much closer to that of K1 kapt. + +To use the new K2 kapt plugin implementation, enable it just like you did the previous K2 kapt plugin. +Add the following option to the `gradle.properties` file of your project: + +```kotlin +kapt.use.k2=true +``` + +In upcoming releases, the K2 kapt implementation will be enabled by default instead of K1 kapt, +so you will no longer need to enable it manually. + +When using the K2 kapt plugin, you might encounter a compilation error during the `kaptGenerateStubs*` tasks, +even though the actual error details are missing from the Gradle log. +This is a [known issue](https://youtrack.jetbrains.com/issue/KT-71431) that occurs when kapt is enabled in a module, +but no annotation processors are present. The workaround is to disable the kapt plugin in the module. + +We highly appreciate your [feedback](https://youtrack.jetbrains.com/issue/KT-71439/K2-kapt-feedback) before the new implementation is stabilized. + +### Resolution for overload conflicts between unsigned and non-primitive types + +This release addresses the issue of resolution for overload conflicts +that could occur in previous versions when functions were overloaded for unsigned and non-primitive types, +as in the following examples: + +#### Overloaded extension functions + +```kotlin +fun Any.doStuff() = "Any" +fun UByte.doStuff() = "UByte" + +fun main() { + val uByte: UByte = UByte.MIN_VALUE + uByte.doStuff() // Overload resolution ambiguity before Kotlin 2.1.0 +} +``` + +In earlier versions, calling `uByte.doStuff()` led to ambiguity because both the `Any` and `UByte` extensions were applicable. + +#### Overloaded top-level functions + +```kotlin +fun doStuff(value: Any) = "Any" +fun doStuff(value: UByte) = "UByte" + +fun main() { + val uByte: UByte = UByte.MIN_VALUE + doStuff(uByte) // Overload resolution ambiguity before Kotlin 2.1.0 +} +``` + +Similarly, the call to `doStuff(uByte)` was ambiguous because the compiler couldn't decide whether to use the `Any` or `UByte` version. +With 2.1.0, the compiler now handles these cases correctly, resolving the ambiguity by giving precedence to the more specific type, +in this case `UByte`. + +## Kotlin/JVM + +Starting with version 2.1.0, the compiler can generate classes containing Java 23 bytecode. + +### Change of JSpecify nullability mismatch diagnostics severity to strict + +Kotlin 2.1.0 enforces strict handling of nullability annotations from `org.jspecify.annotations`, +improving type safety for Java interoperability. + +The following nullability annotations are affected: + +* `org.jspecify.annotations.Nullable` +* `org.jspecify.annotations.NonNull` +* `org.jspecify.annotations.NullMarked` +* Legacy annotations in `org.jspecify.nullness` (JSpecify 0.2 and earlier) + +Starting from Kotlin 2.1.0, nullability mismatches are raised from warnings to errors by default. +This ensures that annotations like `@NonNull` and `@Nullable` are enforced during type checks, +preventing unexpected nullability issues at runtime. + +The `@NullMarked` annotation also affects the nullability of all members within its scope, +making the behavior more predictable when you're working with annotated Java code. + +Here's an example demonstrating the new default behavior: + +```java +// Java +import org.jspecify.annotations.*; +public class SomeJavaClass { + @NonNull + public String foo() { //... + } + + @Nullable + public String bar() { //... + } +} +``` + +```kotlin +// Kotlin +fun test(sjc: SomeJavaClass) { + // Accesses a non-null result, which is allowed + sjc.foo().length + + // Raises an error in the default strict mode because the result is nullable + // To avoid the error, use ?.length instead + sjc.bar().length +} +``` + +You can manually control the severity of diagnostics for these annotations. +To do so, use the `-Xnullability-annotations` compiler option to choose a mode: + +* `ignore`: Ignore nullability mismatches. +* `warning`: Report warnings for nullability mismatches. +* `strict`: Report errors for nullability mismatches (default mode). + +For more information, see [Nullability annotations](java-interop.md#nullability-annotations). + +## Kotlin Multiplatform + +Kotlin 2.1.0 introduces [basic support for Swift export](#basic-support-for-swift-export) and makes +[publishing Kotlin Multiplatform libraries easier](#ability-to-publish-kotlin-libraries-from-any-host). +It also focuses on improvements around Gradle that stabilize the [new DSL for configuring compiler options](#new-gradle-dsl-for-compiler-options-in-multiplatform-projects-promoted-to-stable) +and bring a [preview of the Isolated Projects feature](#preview-gradle-s-isolated-projects-in-kotlin-multiplatform). + +### New Gradle DSL for compiler options in multiplatform projects promoted to Stable + +In Kotlin 2.0.0, [we introduced a new Experimental Gradle DSL](whatsnew20.md#new-gradle-dsl-for-compiler-options-in-multiplatform-projects) +to simplify the configuration of compiler options across your multiplatform projects. +In Kotlin 2.1.0, this DSL has been promoted to Stable. + +The overall project configuration now has three layers. The highest is the extension level, +then the target level, and the lowest is the compilation unit (which is usually a compilation task): + +![Kotlin compiler options levels](compiler-options-levels.svg){width=700} + +To learn more about the different levels and how compiler options can be configured between them, +see [Compiler options](multiplatform-dsl-reference.md#compiler-options). + +### Preview Gradle's Isolated Projects in Kotlin Multiplatform + +> This feature is [Experimental](components-stability.md#stability-levels-explained) and is currently in a pre-Alpha state in Gradle. +> Use it only with Gradle version 8.10 and solely for evaluation purposes. The feature may be dropped or changed at any time. +> +> We would appreciate your feedback on it in [YouTrack](https://youtrack.jetbrains.com/issue/KT-57279/Support-Gradle-Project-Isolation-Feature-for-Kotlin-Multiplatform). +> Opt-in is required (see details below). +> +{style="warning"} + +In Kotlin 2.1.0, +you can preview Gradle's [Isolated Projects](https://docs.gradle.org/current/userguide/isolated_projects.html) +feature in your multiplatform projects. + +The Isolated Projects feature in Gradle improves build performance by "isolating" +configuration of individual Gradle projects from each other. +Each project's build logic is restricted from directly accessing the mutable state of other projects, +allowing them to safely run in parallel. +To support this feature, we made some changes to the Kotlin Gradle plugin's model, +and we are interested in hearing about your experiences during this preview phase. + +There are two ways to enable the Kotlin Gradle plugin's new model: + +* Option 1: **Testing compatibility without enabling Isolated Projects** – + To check compatibility with the Kotlin Gradle plugin's new model without enabling the Isolated Projects feature, + add the following Gradle property in the `gradle.properties` file of your project: + + ```none + # gradle.properties + kotlin.kmp.isolated-projects.support=enable + ``` + +* Option 2: **Testing with Isolated Projects enabled** – + Enabling the Isolated Projects feature in Gradle automatically configures the Kotlin Gradle plugin to use the new model. + To enable the Isolated Projects feature, [set the system property](https://docs.gradle.org/current/userguide/isolated_projects.html#how_do_i_use_it). + In this case, you don't need to add the Gradle property for the Kotlin Gradle plugin to your project. + +### Basic support for Swift export + +Version 2.1.0 takes the first step towards providing support for Swift export in Kotlin, +allowing you to export Kotlin sources directly to the Swift interface without using Objective-C headers. +This should make multiplatform development for Apple targets easier. + +The current basic support includes the ability to: + +* Export multiple Gradle modules from Kotlin directly to Swift. +* Define custom Swift module names with the `moduleName` property. +* Set collapse rules for the package structure with the `flattenPackage` property. + +You can use the following build file in your project as a starting point for setting up Swift export: + +```kotlin +// build.gradle.kts +kotlin { + + iosX64() + iosArm64() + iosSimulatorArm64() + + @OptIn(ExperimentalSwiftExportDsl::class) + swiftExport { + // Root module name + moduleName = "Shared" + + // Collapse rule + // Removes package prefix from generated Swift code + flattenPackage = "com.example.sandbox" + + // Export external modules + export(project(":subproject")) { + // Exported module name + moduleName = "Subproject" + // Collapse exported dependency rule + flattenPackage = "com.subproject.library" + } + + // Configure Swift export binaries + binaries { + linkTaskProvider.configure { + freeCompilerArgs += "-opt-in=some.value" + } + } + } +} +``` + +You can also clone our [public sample](https://github.com/Kotlin/swift-export-sample) with Swift export already set up. + +The compiler automatically generates all the necessary files (including `swiftmodule` files, +static `a` library, and header and `modulemap` files) and copies them into the app's build directory, +which you can access from Xcode. + +#### How to enable Swift export + +This feature is currently [Experimental](components-stability.md#stability-levels-explained). +To try it out in your project, add the following Gradle option to your `gradle.properties` file: + +```none +# gradle.properties +kotlin.experimental.swift-export.enabled=true +``` + +#### Leave feedback on Swift export + +We're planning to expand and stabilize Swift export support in future Kotlin releases. +Please leave your feedback in [this YouTrack issue](https://youtrack.jetbrains.com/issue/KT-64572). + +### Ability to publish Kotlin libraries from any host + +> This feature is currently [Experimental](components-stability.md#stability-levels-explained). +> Opt-in is required (see the details below), and you should use it only for evaluation purposes. +> We would appreciate your feedback on it in [YouTrack](https://youtrack.jetbrains.com/issue/KT-71290). +> +{style="warning"} + +The Kotlin compiler produces `.klib` artifacts for publishing Kotlin libraries. +Previously, you could get the necessary artifacts from any host, except for Apple platform targets that required a Mac machine. +That put a special restraint on Kotlin Multiplatform projects that targeted iOS, macOS, tvOS, and watchOS targets. + +Kotlin 2.1.0 lifts this restriction, achieving full support for cross-compilation. +Now you can use any host to produce `.klib` artifacts, +which should greatly simplify the publishing process for Kotlin and Kotlin Multiplatform libraries. + +> To build [final binaries](multiplatform-build-native-binaries.md) for Apple targets, you still need to use a Mac machine. +> +{style="note"} + +For more information, see [Publishing multiplatform libraries](https://kotlinlang.org/docs/multiplatform-publish-lib.html). + +#### How to enable the publishing Kotlin libraries from any host feature + +This feature is currently [Experimental](components-stability.md#stability-levels-explained). +To try it out in your project, add the following binary option to your `gradle.properties` file: + +```none +# gradle.properties +kotlin.native.enableKlibsCrossCompilation=true +``` + +#### Leave feedback on the publishing Kotlin libraries from any host feature + +We're planning to stabilize this feature and further improve library publication in future Kotlin releases. +Please leave your feedback in our issue tracker [YouTrack](https://youtrack.jetbrains.com/issue/KT-71290). + +### Support for non-packed klibs + +Kotlin 2.1.0 makes it possible to generate non-packed `.klib` file artifacts. +This gives you the option to configure dependencies on klibs directly rather than unpack them first. + +This change can also improve performance, +decreasing compilation and linking time in your Kotlin/Wasm, Kotlin/JS, and Kotlin/Native projects. + +For example, +our benchmark shows a performance improvement of roughly 3% in total build time on the project with 1 linking and 10 compilation tasks +(the project builds a single native executable binary that depends on 9 simplified projects). +However, the actual impact on build time depends on both the number of subprojects and their respective sizes. + +#### How to set up your project + +By default, Kotlin compilation and linking tasks are now configured to use the new non-packed artifacts. + +If you have set up custom build logic for resolving klibs and want to use the new unpacked artifacts, +you need to explicitly specify the preferred variant of klib package resolution in your Gradle build file: + +```kotlin +// build.gradle.kts +import org.jetbrains.kotlin.gradle.plugin.attributes.KlibPackaging +// ... +val resolvableConfiguration = configurations.resolvable("resolvable") { + + // For the new non-packed configuration: + attributes.attribute(KlibPackaging.ATTRIBUTE, project.objects.named(KlibPackaging.NON_PACKED)) + + // For the previous packed configuration: + attributes.attribute(KlibPackaging.ATTRIBUTE, project.objects.named(KlibPackaging.PACKED)) +} +``` + +Non-packed `.klib` files are generated at the same path in your project's build directory as the packed ones previously were. +In turn, packed klibs are now located in the `build/libs` directory. + +If no attribute is specified, the packed variant is used. +You can check the list of available attributes and variants with the following console command: + +```shell +./gradlew outgoingVariants +``` + +We would appreciate your feedback on this feature in [YouTrack](https://kotl.in/issue). + +### Further deprecation of old `android` target + +In Kotlin 2.1.0, the deprecation warning for the old `android` target name has been raised to an error. + +Currently, we recommend using the `androidTarget` option in your Kotlin Multiplatform projects targeting Android. +This is a temporary solution that is necessary to free the `android` name for the upcoming Android/KMP plugin from Google. + +We'll provide further migration instructions when the new plugin is available. +The new DSL from Google will be the preferred option for Android target support in Kotlin Multiplatform. + +For more information, +see the [Kotlin Multiplatform compatibility guide](multiplatform-compatibility-guide.md#rename-of-android-target-to-androidtarget). + +### Dropped support for declaring multiple targets of the same type + +Before Kotlin 2.1.0, you could declare multiple targets of the same type in your multiplatform projects. +However, this made it challenging to distinguish between targets and to support shared source sets effectively. +In most cases, a simpler setup, such as using separate Gradle projects, works better. +For detailed guidance and an example of how to migrate, +see [Declaring several similar targets](multiplatform-compatibility-guide.md#declaring-several-similar-targets) +in the Kotlin Multiplatform compatibility guide. + +Kotlin 1.9.20 triggered a deprecation warning if you declared multiple targets of the same type in your multiplatform projects. +In Kotlin 2.1.0, this deprecation warning is now an error for all targets except Kotlin/JS ones. +To learn more about why Kotlin/JS targets are exempt, +see this issue in [YouTrack](https://youtrack.jetbrains.com/issue/KT-47038/KJS-MPP-Split-JS-target-into-JsBrowser-and-JsNode). + +## Kotlin/Native + +Kotlin 2.1.0 includes an [upgrade for the `iosArm64` target support](#iosarm64-promoted-to-tier-1), +[improved cinterop caching process](#changes-to-caching-in-cinterop), and other updates. + +### iosArm64 promoted to Tier 1 + +The `iosArm64` target, which is crucial for [Kotlin Multiplatform](multiplatform-intro.md) development, +has been promoted to Tier 1. This is the highest level of support in the Kotlin/Native compiler. + +This means the target is regularly tested on the CI pipeline to ensure that it's able to compile and run. +We also provide source and binary compatibility between compiler releases for the target. + +For more information on target tiers, see [Kotlin/Native target support](native-target-support.md). + +### LLVM update from 11.1.0 to 16.0.0 + +In Kotlin 2.1.0, we updated LLVM from version 11.1.0 to 16.0.0. +The new version includes bug fixes and security updates. +In certain cases, it also provides compiler optimizations and faster compilation. + +If you have Linux targets in your project, +take note that the Kotlin/Native compiler now uses the `lld` linker by default for all Linux targets. + +This update shouldn't affect your code, but if you encounter any issues, please report them to our [issue tracker](http://kotl.in/issue). + +### Changes to caching in cinterop + +In Kotlin 2.1.0, we're making changes to the cinterop caching process. It no longer has the +[`CacheableTask`](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/CacheableTask.html) annotation type. +The new recommended approach is to use the [`cacheIf`](https://docs.gradle.org/current/kotlin-dsl/gradle/org.gradle.api.tasks/-task-outputs/cache-if.html) +output type to cache the results of the task. + +This should resolve issues where `UP-TO-DATE` +checks failed to detect changes to header files specified in the [definition file](native-definition-file.md), +preventing the build system from recompiling the code. + +### Deprecation of the mimalloc memory allocator + +Back in Kotlin 1.9.0, we introduced the new memory allocator, and then we enabled it by default in Kotlin 1.9.20. +The new allocator has been designed to make garbage collection more efficient +and improve the Kotlin/Native memory manager's runtime performance. + +The new memory allocator replaced the previous default allocator, [mimalloc](https://github.com/microsoft/mimalloc). +Now, it's time to deprecate mimalloc in the Kotlin/Native compiler. + +You can now remove the `-Xallocator=mimalloc` compiler option from your build scripts. +If you encounter any issues, please report them to our [issue tracker](http://kotl.in/issue). + +For more information on the memory allocator and garbage collection in Kotlin, +see [Kotlin/Native memory management](native-memory-manager.md). + +## Kotlin/Wasm + +Kotlin/Wasm received multiple updates along with [support for incremental compilation](#support-for-incremental-compilation). + +### Support for incremental compilation + +Previously, when you changed something in your Kotlin code, the Kotlin/Wasm toolchain had to recompile the entire codebase. + +Starting from 2.1.0, incremental compilation is supported for Wasm targets. +In development tasks, the compiler now recompiles only files relevant to changes from the last compilation, +which noticeably reduces the compilation time. + +This change currently doubles the compilation speed, and there are plans to improve it further in future releases. + +In the current setup, incremental compilation for Wasm targets is disabled by default. +To enable incremental compilation, add the following line to your project's `local.properties` or `gradle.properties` file: + +```none +# gradle.properties +kotlin.incremental.wasm=true +``` + +Try out Kotlin/Wasm incremental compilation +and [share your feedback](https://youtrack.jetbrains.com/issue/KT-72158/Kotlin-Wasm-incremental-compilation-feedback). +Your insights will help make this feature Stable and enabled by default sooner. + +### Browser APIs moved to the kotlinx-browser stand-alone library + +Previously, the declarations for web APIs and related target utilities were part of the Kotlin/Wasm standard library. + +In this release, the `org.w3c.*` +declarations have been moved from the Kotlin/Wasm standard library to the new [kotlinx-browser library](https://github.com/kotlin/kotlinx-browser). +This library also includes other web-related packages, such as `org.khronos.webgl`, `kotlin.dom`, and `kotlinx.browser`. + +This separation provides modularity, enabling independent updates for web-related APIs outside of Kotlin's release cycle. +Additionally, the Kotlin/Wasm standard library now contains only declarations available in any JavaScript environment. + +To use the declarations from the moved packages, +you need to add the `kotlinx-browser` dependency to your project's build configuration file: + +```kotlin +// build.gradle.kts +val wasmJsMain by getting { + dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-browser:0.3") + } +} +``` + +### Improved debugging experience for Kotlin/Wasm + +Previously, when debugging Kotlin/Wasm code in web browsers, you might have encountered +a low-level representation of variable values in the debugging interface. +This often made it challenging to track the current state of the application. + +![Kotlin/Wasm old debugger](wasm-old-debugger.png){width=700} + +To improve this experience, custom formatters have been added in the variable view. +The implementation uses the [custom formatters API](https://firefox-source-docs.mozilla.org/devtools-user/custom_formatters/index.html), +which is supported across major browsers like Firefox and Chromium-based ones. + +With this change, you can now display and locate variable values in a more user-friendly and comprehensible manner. + +![Kotlin/Wasm improved debugger](wasm-debugger-improved.png){width=700} + +To try the new debugging experience: + +1. Add the following compiler option to the `wasmJs {}` compiler options: + + ```kotlin + // build.gradle.kts + kotlin { + wasmJs { + // ... + + compilerOptions { + freeCompilerArgs.add("-Xwasm-debugger-custom-formatters") + } + } + } + ``` + +2. Enable custom formatters in your browser: + + * In Chrome DevTools, it's available via **Settings | Preferences | Console**: + + ![Enable custom formatters in Chrome](wasm-custom-formatters-chrome.png){width=700} + + * In Firefox DevTools, it's available via **Settings | Advanced settings**: + + ![Enable custom formatters in Firefox](wasm-custom-formatters-firefox.png){width=700} + +### Reduced size of Kotlin/Wasm binaries + +The size of your Wasm binaries produced by production builds will be reduced by up to 30%, +and you may see some performance improvements. +This is because the `--closed-world`, `--type-ssa`, and `--type-merging` +Binaryen options are now considered safe to use for all Kotlin/Wasm projects and are enabled by default. + +### Improved JavaScript array interoperability in Kotlin/Wasm + +While Kotlin/Wasm's standard library provides the `JsArray` type for JavaScript arrays, +there was no direct method to transform `JsArray` into Kotlin's native `Array` or `List` types. + +This gap required creating custom functions for array transformations, complicating interoperability between Kotlin and JavaScript code. + +This release introduces an adapter function that automatically converts `JsArray` to `Array` and vice versa, +simplifying array operations. + +Here's an example of conversion between generic types: Kotlin `List `and `Array` to JavaScript `JsArray.` + +```kotlin +val list: List = + listOf("Kotlin", "Wasm").map { it.toJsString() } + +// Uses .toJsArray() to convert List or Array to JsArray +val jsArray: JsArray = list.toJsArray() + +// Uses .toArray() and .toList() to convert it back to Kotlin types +val kotlinArray: Array = jsArray.toArray() +val kotlinList: List = jsArray.toList() +``` + +Similar methods are available for converting typed arrays to their Kotlin equivalents +(for example, `IntArray` and `Int32Array`). For detailed information and implementation, +see the [`kotlinx-browser` repository]( https://github.com/Kotlin/kotlinx-browser/blob/dfbdceed314567983c98f1d66e8c2e10d99c5a55/src/wasmJsMain/kotlin/arrayCopy.kt). + +Here's an example of conversion between typed arrays: Kotlin `IntArray` to JavaScript `Int32Array`. + +```kotlin +import org.khronos.webgl.* + + // ... + + val intArray: IntArray = intArrayOf(1, 2, 3) + + // Uses .toInt32Array() to convert Kotlin IntArray to JavaScript Int32Array + val jsInt32Array: Int32Array = intArray.toInt32Array() + + // Uses toIntArray() to convert JavaScript Int32Array back to Kotlin IntArray + val kotlinIntArray: IntArray = jsInt32Array.toIntArray() +``` + +### Support for accessing JavaScript exception details in Kotlin/Wasm + +Previously, when a JavaScript exception occurred in Kotlin/Wasm, +the `JsException` type provided only a generic message without details from the original JavaScript error. + +Starting from Kotlin 2.1.0, you can configure `JsException` +to include the original error message and stack trace by enabling a specific compiler option. +This provides more context to help diagnose issues originating from JavaScript. + +This behavior depends on the `WebAssembly.JSTag` API, which is available only in certain browsers: + +* **Chrome**: Supported from version 115 +* **Firefox**: Supported from version 129 +* **Safari**: Not yet supported + +To enable this feature, which is disabled by default, +add the following compiler option to your `build.gradle.kts` file: + +```kotlin +// build.gradle.kts +kotlin { + wasmJs { + compilerOptions { + freeCompilerArgs.add("-Xwasm-attach-js-exception") + } + } +} +``` + +Here's an example demonstrating the new behavior: + +```kotlin +external object JSON { + fun parse(json: String): T +} + +fun main() { + try { + JSON.parse("an invalid JSON") + } catch (e: JsException) { + println("Thrown value is: ${e.thrownValue}") + // SyntaxError: Unexpected token 'a', "an invalid JSON" is not valid JSON + + println("Message: ${e.message}") + // Message: Unexpected token 'a', "an invalid JSON" is not valid JSON + + println("Stacktrace:") + // Stacktrace: + + // Prints the full JavaScript stack trace + e.printStackTrace() + } +} +``` + +With the `-Xwasm-attach-js-exception` option enabled, `JsException` provides specific details from the JavaScript error. +Without the option, `JsException` includes only a generic message stating that an exception was thrown while running JavaScript code. + +### Deprecation of default exports + +As part of the migration to named exports, +an error was previously printed to the console when a default import was used for Kotlin/Wasm exports in JavaScript. + +In 2.1.0, default imports have been completely removed to fully support named exports. + +When coding in JavaScript for the Kotlin/Wasm target, you now need to use the corresponding named imports instead of default imports. + +This change marks the last phase of a deprecation cycle to migrate to named exports: + +**In version 2.0.0:** A warning message was printed to the console, explaining that exporting entities via default exports is deprecated. + +**In version 2.0.20:** An error occurred, requesting the use of the corresponding named import. + +**In version 2.1.0:** The use of default imports has been completely removed. + +### Subproject-specific Node.js settings + +You can configure Node.js settings for your project by defining properties of the `NodeJsRootPlugin` class for `rootProject`. +In 2.1.0, you can configure these settings for each subproject using a new class, `NodeJsPlugin`. +Here's an example demonstrating how to set a specific Node.js version for a subproject: + +```kotlin +// build.gradle.kts +project.plugins.withType { + project.the().version = "22.0.0" +} +``` + +To use the new class for the entire project, add the same code in the `allprojects {}` block: + +```kotlin +// build.gradle.kts +allprojects { + project.plugins.withType { + project.the().version = "your Node.js version" + } +} +``` + +You can also use Gradle convention plugins to apply the settings to a particular set of subprojects. + +## Kotlin/JS + +### Support for non-identifier characters in properties + +Kotlin/JS previously did not allow using [names for test methods](coding-conventions.md#names-for-test-methods) with spaces enclosed in backticks. + +Similarly, it was not possible to access JavaScript object properties that contained characters not permitted in Kotlin identifiers, +such as hyphens or spaces: + +```kotlin +external interface Headers { + var accept: String? + + // Invalid Kotlin identifier due to hyphen + var `content-length`: String? +} + +val headers: Headers = TODO("value provided by a JS library") +val accept = headers.accept +// Causes error due to the hyphen in property name +val length = headers.`content-length` +``` + +This behavior differed from JavaScript and TypeScript, which allow such properties to be accessed using non-identifier characters. + +Starting from Kotlin 2.1.0, this feature is enabled by default. +Kotlin/JS now allows you to use the backticks (``) and the `@JsName` annotation +to interact with JavaScript properties containing non-identifier characters and to use names for test methods. + +Additionally, +you can use the `@JsName` and ` @JsQualifier` annotations to map Kotlin property names to JavaScript equivalents: + +```kotlin +object Bar { + val `property example`: String = "bar" +} + +@JsQualifier("fooNamespace") +external object Foo { + val `property example`: String +} + +@JsExport +object Baz { + val `property example`: String = "bar" +} + +fun main() { + // In JavaScript, this is compiled into Bar.property_example_HASH + println(Bar.`property example`) + // In JavaScript, this is compiled into fooNamespace["property example"] + println(Foo.`property example`) + // In JavaScript, this is compiled into Baz["property example"] + println(Baz.`property example`) +} +``` + +### Support for generating ES2015 arrow functions + +In Kotlin 2.1.0, Kotlin/JS introduces support for generating ES2015 arrow functions, +such as `(a, b) => expression`, instead of anonymous functions. + +Using arrow functions can reduce the bundle size of your project, +especially when using the experimental `-Xir-generate-inline-anonymous-functions` mode. +This also makes the generated code more aligned with modern JS. + +This feature is enabled by default when targeting ES2015. +Alternatively, you can enable it by using the `-Xes-arrow-functions` command line argument. + +Learn more about [ES2015 (ECMAScript 2015, ES6) in the official documentation](https://262.ecma-international.org/6.0/). + +## Gradle improvements + +Kotlin 2.1.0 is fully compatible with Gradle 7.6.3 through 8.6. +Gradle versions 8.7 to 8.10 are also supported, with only one exception. +If you use the Kotlin Multiplatform Gradle plugin, +you may see deprecation warnings in your multiplatform projects calling the [`withJava()` function in the JVM target](multiplatform-dsl-reference.md#jvm-targets). +We plan to fix this issue as soon as possible. + +For more information, +see the related issue in [YouTrack](https://youtrack.jetbrains.com/issue/KT-66542). + +You can also use Gradle versions up to the latest Gradle release, +but if you do, keep in mind that you might encounter deprecation warnings or some new Gradle features might not work. + +### Minimum supported AGP version bumped to 7.3.1 + +Starting with Kotlin 2.1.0, the minimum supported Android Gradle plugin version is 7.3.1. + +### Minimum supported Gradle version bumped to 7.6.3 + +Starting with Kotlin 2.1.0, the minimum supported Gradle version is 7.6.3. + +### New API for Kotlin Gradle plugin extensions + +Kotlin 2.1.0 introduces a new API to make it easier to create your own plugins for configuring the Kotlin Gradle plugin. +This change deprecates the `KotlinTopLevelExtension` and `KotlinTopLevelExtensionConfig` +interfaces and introduces the following interfaces for plugin authors: + +| Name | Description | +|--------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `KotlinBaseExtension` | A plugin DSL extension type for configuring common Kotlin JVM, Android, and Multiplatform plugin options for the entire project:
  • `org.jetbrains.kotlin.jvm`
  • `org.jetbrains.kotlin.android`
  • `org.jetbrains.kotlin.multiplatform`
  • | +| `KotlinJvmExtension` | A plugin DSL extension type for configuring Kotlin **JVM** plugin options for the entire project. | +| `KotlinAndroidExtension` | A plugin DSL extension type for configuring Kotlin **Android** plugin options for the entire project. | + +For example, if you want to configure compiler options for both JVM and Android projects, use `KotlinBaseExtension`: + +```kotlin +configure { + if (this is HasConfigurableKotlinCompilerOptions<*>) { + with(compilerOptions) { + if (this is KotlinJvmCompilerOptions) { + jvmTarget.set(JvmTarget.JVM_17) + } + } + } +} +``` + +This configures the JVM target to 17 for both JVM and Android projects. + +To configure compiler options specifically for JVM projects, use `KotlinJvmExtension`: + +```kotlin +configure { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + } + + target.mavenPublication { + groupId = "com.example" + artifactId = "example-project" + version = "1.0-SNAPSHOT" + } +} +``` + +This example similarly configures the JVM target to 17 for JVM projects. +It also configures a Maven publication for the project so that its output is published to a Maven repository. + +You can use the `KotlinAndroidExtension` in exactly the same way. + +### Compiler symbols hidden from the Kotlin Gradle plugin API + +Starting with Kotlin 2.1.0, +you will receive a warning if you access compiler module symbols bundled within the Kotlin Gradle plugin (KGP). +Previously, the KGP included `org.jetbrains.kotlin:kotlin-compiler-embeddable` in its runtime dependencies, +making internal compiler symbols, such as `KotlinCompilerVersion`, available in the build script classpath. + +These symbols are intended for internal use only. +Access to them will be removed in upcoming Kotlin releases to prevent compatibility issues and simplify KGP maintenance. +If your build logic relies on any compiler symbols, +you need to update it and use the [Gradle Workers API](https://docs.gradle.org/current/userguide/worker_api.html) +with classloader or process isolation to ensure safe interaction with the compiler. + +#### Using the Gradle Workers API + +This example demonstrates how to safely use the Kotlin compiler in a project producing a Gradle plugin. +First, add a compile-only dependency in your build script. +This makes the symbol available at compile time only: + +```kotlin +// build.gradle.kts +dependencies { + compileOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable:%kotlinVersion%") +} +``` + +Next, define a Gradle work action to print the Kotlin compiler version: + +```kotlin +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters +import org.jetbrains.kotlin.config.KotlinCompilerVersion +abstract class ActionUsingKotlinCompiler : WorkAction { + override fun execute() { + println("Kotlin compiler version: ${KotlinCompilerVersion.getVersion()}") + } +} +``` + +Now create a task that submits this action to the worker executor using classloader isolation: + +```kotlin +import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.TaskAction +import org.gradle.workers.WorkerExecutor +import javax.inject.Inject +abstract class TaskUsingKotlinCompiler: DefaultTask() { + @get:Inject + abstract val executor: WorkerExecutor + + @get:Classpath + abstract val kotlinCompiler: ConfigurableFileCollection + + @TaskAction + fun compile() { + val workQueue = executor.classLoaderIsolation { + classpath.from(kotlinCompiler) + } + workQueue.submit(ActionUsingKotlinCompiler::class.java) {} + } +} +``` + +Finally, configure the Kotlin compiler classpath in your Gradle plugin: + +```kotlin +import org.gradle.api.Plugin +import org.gradle.api.Project +abstract class MyPlugin: Plugin { + override fun apply(target: Project) { + val myDependencyScope = target.configurations.create("myDependencyScope") + target.dependencies.add(myDependencyScope.name, "$KOTLIN_COMPILER_EMBEDDABLE:$KOTLIN_COMPILER_VERSION") + val myResolvableConfiguration = target.configurations.create("myResolvable") { + extendsFrom(myDependencyScope) + } + target.tasks.register("myTask", TaskUsingKotlinCompiler::class.java) { + kotlinCompiler.from(myResolvableConfiguration) + } + } + + companion object { + const val KOTLIN_COMPILER_EMBEDDABLE = "org.jetbrains.kotlin:kotlin-compiler-embeddable" + const val KOTLIN_COMPILER_VERSION = "%kotlinVersion%" + } +} +``` + +## Compose compiler updates + +### Support for multiple stability configuration files + +The Compose compiler can interpret multiple stability configuration files, +but the `stabilityConfigurationFile` option of the Compose Compiler Gradle plugin previously allowed +for only a single file to be specified. +In Kotlin 2.1.0, this functionality was reworked to allow you to use several stability configuration files for a single module: + +* The `stabilityConfigurationFile` option is deprecated. +* There is a new option, `stabilityConfigurationFiles`, with the type `ListProperty`. + +Here's how to pass several files to the Compose compiler using the new option: + +```kotlin +// build.gradle.kt +composeCompiler { + stabilityConfigurationFiles.addAll( + project.layout.projectDirectory.file("configuration-file1.conf"), + project.layout.projectDirectory.file("configuration-file2.conf"), + ) +} +``` + +### Pausable composition + +Pausable composition is a new Experimental feature that changes how the compiler generates skippable functions. +With this feature enabled, composition can be suspended on skipping points during runtime, +allowing long-running composition processes to be split across multiple frames. +Pausable composition is used in lazy lists and other performance-intensive components for prefetching content +that might cause frames to drop when executed in a blocking manner. + +To try out pausable composition, add the following feature flag in the Gradle configuration for the Compose compiler: + +```kotlin +// build.gradle.kts +composeCompiler { + featureFlags = setOf( + ComposeFeatureFlag.PausableComposition + ) +} +``` + +> Runtime support for this feature was added in the 1.8.0-alpha02 version of `androidx.compose.runtime`. +> The feature flag has no effect when used with older runtime versions. +> +{style="note"} + +### Changes to open and overridden @Composable functions + +Virtual (open, abstract, and overridden) `@Composable` functions can no longer be restartable. +The codegen for restartable groups was generating calls that [did not work correctly](https://issuetracker.google.com/329477544) +with inheritance, resulting in runtime crashes. + +This means that virtual functions won't be restarted or skipped: +whenever their state is invalidated, runtime will recompose their parent composable instead. +If your code is sensitive to recompositions, you may notice changes in runtime behavior. + +### Performance improvements + +The Compose compiler used to create a full copy of module's IR to transform `@Composable` types. +Apart from increased memory consumption when copying elements that were not related to Compose, +this behavior was also breaking downstream compiler plugins in [certain edge cases](https://issuetracker.google.com/365066530). + +This copy operation was removed, resulting in potentially faster compilation times. + +## Standard library + +### Changes to the deprecation severity of standard library APIs + +In Kotlin 2.1.0, we are raising the deprecation severity level of several standard library APIs from warning to error. +If your code relies on these APIs, you need to update it to ensure compatibility. +The most notable changes include: + +* **Locale-sensitive case conversion functions for `Char` and `String` are deprecated:** + Functions like `Char.toLowerCase()`, `Char.toUpperCase()`, `String.toUpperCase()`, + and `String.toLowerCase()` are now deprecated, and using them results in an error. + Replace them with locale-agnostic function alternatives or other case conversion mechanisms. + If you want to continue using the default locale, replace calls like `String.toLowerCase()` + with `String.lowercase(Locale.getDefault())`, explicitly specifying the locale. + For a locale-agnostic conversion, replace them with `String.lowercase()`, which uses the invariant locale by default. + +* **Kotlin/Native freezing API is deprecated:** + Using the freezing-related declarations previously marked with the `@FreezingIsDeprecated` annotation now results in an error. + This change reflects the transition from the legacy memory manager in Kotlin/Native, + which required freezing objects to share them between threads. + To learn how to migrate from freezing-related APIs in the new memory model, + see the [Kotlin/Native migration guide](native-migration-guide.md#update-your-code). + For more information, see the [announcement about the deprecation of freezing](whatsnew1720.md#freezing). + +* **`appendln()` is deprecated in favor of `appendLine()`:** + The `StringBuilder.appendln()` and `Appendable.appendln()` functions are now deprecated, and using them results in an error. + To replace them, use the `StringBuilder.appendLine()` or `Appendable.appendLine()` functions instead. + The `appendln()` function is deprecated because, on Kotlin/JVM, it uses the `line.separator` system property, + which has a different default value on each OS. On Kotlin/JVM, this property defaults to `\r\n` (CR LF) on Windows and `\n` (LF) on other systems. + On the other hand, the `appendLine()` function consistently uses `\n` (LF) as the line separator, ensuring consistent behavior across platforms. + +For a complete list of affected APIs in this release, see the [KT-71628](https://youtrack.jetbrains.com/issue/KT-71628) YouTrack issue. + +### Stable file tree traversal extensions for java.nio.file.Path + +Kotlin 1.7.20 introduced Experimental [extension functions](extensions.md#extension-functions) for the `java.nio.file.Path` class, +which allows you to walk through a file tree. +In Kotlin 2.1.0, the following file tree traversal extensions are now [Stable](components-stability.md#stability-levels-explained): + +* `walk()` lazily traverses the file tree rooted at the specified path. +* `fileVisitor()` makes it possible to create a `FileVisitor` separately. + `FileVisitor` specifies the actions to be performed on directories and files during traversal. +* `visitFileTree(fileVisitor: FileVisitor, ...)` traverses through a file tree, + invoking the specified `FileVisitor` on each encountered entry, and it uses the `java.nio.file.Files.walkFileTree()` function under the hood. +* `visitFileTree(..., builderAction: FileVisitorBuilder.() -> Unit)` creates a `FileVisitor` with the provided `builderAction` + and calls the `visitFileTree(fileVisitor, ...)` function. +* `sealed interface FileVisitorBuilder` allows you to define a custom `FileVisitor` implementation. +* `enum class PathWalkOption` provides traversal options for the `Path.walk()` function. + +The examples below demonstrate how to use these file traversal APIs to create custom `FileVisitor` behaviors, +which allows you to define specific actions for visiting files and directories. + +For instance, you can explicitly create a `FileVisitor` and use it later: + +```kotlin +val cleanVisitor = fileVisitor { + onPreVisitDirectory { directory, attributes -> + // Placeholder: Add logic on visiting directories + FileVisitResult.CONTINUE + } + + onVisitFile { file, attributes -> + // Placeholder: Add logic on visiting files + FileVisitResult.CONTINUE + } +} + +// Placeholder: Add logic here for general setup before traversal +projectDirectory.visitFileTree(cleanVisitor) +``` + +You can also create a `FileVisitor` with the `builderAction` and use it immediately for the traversal: + +```kotlin +projectDirectory.visitFileTree { + // Defines the builderAction: + onPreVisitDirectory { directory, attributes -> + // Some logic on visiting directories + FileVisitResult.CONTINUE + } + + onVisitFile { file, attributes -> + // Some logic on visiting files + FileVisitResult.CONTINUE + } +} +``` + +Additionally, you can traverse a file tree rooted at the specified path with the `walk()` function: + +```kotlin +fun traverseFileTree() { + val cleanVisitor = fileVisitor { + onPreVisitDirectory { directory, _ -> + if (directory.name == "build") { + directory.toFile().deleteRecursively() + FileVisitResult.SKIP_SUBTREE + } else { + FileVisitResult.CONTINUE + } + } + + // Deletes files with the .class extension + onVisitFile { file, _ -> + if (file.extension == "class") { + file.deleteExisting() + } + FileVisitResult.CONTINUE + } + } + + // Sets up the root directory and files + val rootDirectory = createTempDirectory("Project") + + // Creates the src directory with A.kt and A.class files + rootDirectory.resolve("src").let { srcDirectory -> + srcDirectory.createDirectory() + srcDirectory.resolve("A.kt").createFile() + srcDirectory.resolve("A.class").createFile() + } + + // Creates the build directory with a Project.jar file + rootDirectory.resolve("build").let { buildDirectory -> + buildDirectory.createDirectory() + buildDirectory.resolve("Project.jar").createFile() + } + + // Uses the walk() function: + val directoryStructure = rootDirectory.walk(PathWalkOption.INCLUDE_DIRECTORIES) + .map { it.relativeTo(rootDirectory).toString() } + .toList().sorted() + println(directoryStructure) + // "[, build, build/Project.jar, src, src/A.class, src/A.kt]" + + // Traverses the file tree with cleanVisitor, applying the rootDirectory.visitFileTree(cleanVisitor) cleanup rules + val directoryStructureAfterClean = rootDirectory.walk(PathWalkOption.INCLUDE_DIRECTORIES) + .map { it.relativeTo(rootDirectory).toString() } + .toList().sorted() + println(directoryStructureAfterClean) + // "[, src, src/A.kt]" +} +``` + ## Documentation updates The Kotlin documentation has received some notable changes: +### Language concepts + +* Improved [Null safety](null-safety.md) page – Learn how to handle `null` values safely in your code. +* Improved [Objects declarations and expressions](object-declarations.md) page – + Learn how to define a class and create an instance in a single step. +* Improved [When expressions and statements](control-flow.md#when-expressions-and-statements) section – + Learn about the `when` conditional and how you can use it. +* Updated [Kotlin roadmap](roadmap.md), [Kotlin evolution principles](kotlin-evolution-principles.md), + and [Kotlin language features and proposals](kotlin-language-features-and-proposals.md) pages – + Learn about Kotlin's plans, ongoing developments, and guiding principles. + +### Compose compiler + +* [Compose compiler documentation](compose-compiler-migration-guide.md) now located in the Compiler and plugins section – + Learn about the Compose compiler, the compiler options, and the steps to migrate. + +### API references + +* New [Kotlin Gradle plugins API reference](https://kotlinlang.org/api/kotlin-gradle-plugin) – + Explore the API references for the Kotlin Gradle plugin and the Compose compiler Gradle plugin. + +### Multiplatform development + +* New [Building a Kotlin library for multiplatform](api-guidelines-build-for-multiplatform.md) page – + Learn how to design your Kotlin libraries for Kotlin Multiplatform. +* New [Introduction to Kotlin Multiplatform](multiplatform-intro.md) page – Learn about Kotlin Multiplatform's key concepts, dependencies, libraries, and more. +* Updated [Kotlin Multiplatform overview](multiplatform.topic) page – Navigate through the essentials of Kotlin Multiplatform and popular use cases. +* New [iOS integration](multiplatform-ios-integration-overview.md) section – Learn how to integrate a Kotlin Multiplatform shared module into your iOS app. +* New [Kotlin/Native's definition file](native-definition-file.md) page – Learn how to create a definition file to consume C and Objective-C libraries. +* [Get started with WASI](wasm-wasi.md) – + Learn how to run a simple Kotlin/Wasm application using WASI in various WebAssembly virtual machines. + +### Tooling + +* [New Dokka migration guide](dokka-migration.md) – Learn how to migrate to Dokka Gradle plugin v2. + +## Compatibility guide for Kotlin 2.1.0 + +Kotlin 2.1.0 is a feature release and can, therefore, +bring changes that are incompatible with your code written for earlier versions of the language. +Find the detailed list of these changes in the [Compatibility guide for Kotlin 2.1.0](compatibility-guide-21.md). + ## Install Kotlin 2.1.0 Starting from IntelliJ IDEA 2023.3 and Android Studio Iguana (2023.2.1) Canary 15, the Kotlin plugin is distributed as a From 4d0b2f7fba85bfdc9f275f524ad758628c2656f8 Mon Sep 17 00:00:00 2001 From: Andrey Polyakov Date: Wed, 27 Nov 2024 16:35:53 +0100 Subject: [PATCH 32/32] fix: workaround for the api guidelines --- docs/topics/whatsnew21.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/whatsnew21.md b/docs/topics/whatsnew21.md index 28bad751f51..6f17fa6c12e 100644 --- a/docs/topics/whatsnew21.md +++ b/docs/topics/whatsnew21.md @@ -1560,7 +1560,7 @@ The Kotlin documentation has received some notable changes: ### Multiplatform development -* New [Building a Kotlin library for multiplatform](api-guidelines-build-for-multiplatform.md) page – +* New [Building a Kotlin library for multiplatform](https://kotlinlang.org/docs/api-guidelines-build-for-multiplatform.html) page – Learn how to design your Kotlin libraries for Kotlin Multiplatform. * New [Introduction to Kotlin Multiplatform](multiplatform-intro.md) page – Learn about Kotlin Multiplatform's key concepts, dependencies, libraries, and more. * Updated [Kotlin Multiplatform overview](multiplatform.topic) page – Navigate through the essentials of Kotlin Multiplatform and popular use cases.