diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6bf7e65c..f004cf6f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,29 +13,14 @@ jobs: runs-on: ${{matrix.os}} timeout-minutes: 40 steps: - - name: Checkout the repo - uses: actions/checkout@v2 - - name: Set up JDK - uses: DeLaGuardo/setup-graalvm@4.0 - with: - graalvm: 21.2.0 - java: java17 - arch: amd64 - - name: Cache gradle - uses: actions/cache@v2 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Cache konan - uses: actions/cache@v2 - with: - path: ~/.konan - key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Build - run: ./gradlew build --build-cache --no-daemon --stacktrace + - name: Checkout sources + uses: actions/checkout@v4 + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 17 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Build with Gradle + run: ./gradlew build diff --git a/CHANGELOG.md b/CHANGELOG.md index 06dfa5bf..2fa2a40c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,23 @@ ### Security +## 0.10.1 - 2025-02-23 + +### Added + +- Relax requirements for `withDefault` and `view`. Now they could be used with `MutableMetaProvider` +- Add `MutableMetaProvider` to `MutableMeta` converter `asMutableMeta`. + +### Changed + +- Implementation of `Meta::withDefault` +- `scheme` extension limited to `MutableMeta` to avoid resolution ambiguity with `Scheme` + +### Fixed + +- Incorrect work of `MetaWithDefault::getValue` +- Incorrect work of scheme delegate + ## 0.10.0 - 2025-01-19 ### Added diff --git a/README.md b/README.md index a3dd7b7b..07dcacb4 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ In this section, we will try to cover DataForge main ideas in the form of questi > **Maturity**: PROTOTYPE ### [dataforge-workspace](dataforge-workspace) +> A framework for pull-based data processing > > **Maturity**: EXPERIMENTAL diff --git a/build.gradle.kts b/build.gradle.kts index 84c1bba7..cba70f11 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ plugins { allprojects { group = "space.kscience" - version = "0.10.0" + version = "0.10.1" } subprojects { diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataFilter.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataFilter.kt index e9392da8..b41c4f30 100644 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataFilter.kt +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataFilter.kt @@ -77,6 +77,7 @@ public class FilteredDataTree<T>( } } +@OptIn(DFInternal::class) public fun <T> DataTree<T>.filterData( predicate: DataFilter, -): FilteredDataTree<T> = FilteredDataTree(this, predicate, Name.EMPTY) \ No newline at end of file +): DataTree<T> = FilteredDataTree(this, predicate, Name.EMPTY) \ No newline at end of file diff --git a/dataforge-io/README.md b/dataforge-io/README.md index 9b56352e..db7e4547 100644 --- a/dataforge-io/README.md +++ b/dataforge-io/README.md @@ -15,7 +15,7 @@ IO module ## Artifact: -The Maven coordinates of this project are `space.kscience:dataforge-io:0.10.0`. +The Maven coordinates of this project are `space.kscience:dataforge-io:0.10.1`. **Gradle Kotlin DSL:** ```kotlin @@ -25,6 +25,6 @@ repositories { } dependencies { - implementation("space.kscience:dataforge-io:0.10.0") + implementation("space.kscience:dataforge-io:0.10.1") } ``` diff --git a/dataforge-io/build.gradle.kts b/dataforge-io/build.gradle.kts index 52e47bb3..7cc8db2e 100644 --- a/dataforge-io/build.gradle.kts +++ b/dataforge-io/build.gradle.kts @@ -4,8 +4,6 @@ plugins { description = "IO module" -val ioVersion = "0.6.0" - kscience { jvm() js() @@ -17,8 +15,8 @@ kscience { } dependencies { api(projects.dataforgeContext) - api("org.jetbrains.kotlinx:kotlinx-io-core:$ioVersion") - api("org.jetbrains.kotlinx:kotlinx-io-bytestring:$ioVersion") + api(spclibs.kotlinx.io.core) + api(spclibs.kotlinx.io.bytestring) } } diff --git a/dataforge-meta/README.md b/dataforge-meta/README.md index 7eb186ed..8d77eb2b 100644 --- a/dataforge-meta/README.md +++ b/dataforge-meta/README.md @@ -13,7 +13,7 @@ Meta definition and basic operations on meta ## Artifact: -The Maven coordinates of this project are `space.kscience:dataforge-meta:0.10.0`. +The Maven coordinates of this project are `space.kscience:dataforge-meta:0.10.1`. **Gradle Kotlin DSL:** ```kotlin @@ -23,6 +23,6 @@ repositories { } dependencies { - implementation("space.kscience:dataforge-meta:0.10.0") + implementation("space.kscience:dataforge-meta:0.10.1") } ``` diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index b282c77f..a6455c20 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -313,7 +313,7 @@ public final class space/kscience/dataforge/meta/MetaKt { public static final fun iterator (Lspace/kscience/dataforge/meta/Meta;)Ljava/util/Iterator; public static final fun nodeSequence (Lspace/kscience/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; public static final fun valueSequence (Lspace/kscience/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; - public static final fun withDefault (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MetaProvider;)Lspace/kscience/dataforge/meta/Meta; + public static final fun withDefault (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/meta/MetaProvider;)Lspace/kscience/dataforge/meta/Meta; } public abstract interface class space/kscience/dataforge/meta/MetaProvider : space/kscience/dataforge/meta/ValueProvider { @@ -502,7 +502,8 @@ public final class space/kscience/dataforge/meta/MutableMetaKt { public static synthetic fun setIndexed$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V public static final fun toMutableMeta (Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/dataforge/meta/MutableMeta; public static final fun update (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/meta/Meta;)V - public static final fun withDefault (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/MetaProvider;)Lspace/kscience/dataforge/meta/MutableMeta; + public static final fun withDefault (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/meta/MetaProvider;)Lspace/kscience/dataforge/meta/MutableMeta; + public static final fun withDefaults (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Lspace/kscience/dataforge/meta/MetaProvider;)Lspace/kscience/dataforge/meta/MutableMeta; } public abstract interface class space/kscience/dataforge/meta/MutableMetaProvider : space/kscience/dataforge/meta/MetaProvider, space/kscience/dataforge/meta/MutableValueProvider { @@ -521,8 +522,9 @@ public final class space/kscience/dataforge/meta/MutableMetaSerializer : kotlinx } public final class space/kscience/dataforge/meta/MutableMetaViewKt { - public static final fun view (Lspace/kscience/dataforge/meta/MutableMeta;Ljava/lang/String;)Lspace/kscience/dataforge/meta/MutableMeta; - public static final fun view (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMeta; + public static final fun asMutableMeta (Lspace/kscience/dataforge/meta/MutableMetaProvider;)Lspace/kscience/dataforge/meta/MutableMeta; + public static final fun view (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;)Lspace/kscience/dataforge/meta/MutableMeta; + public static final fun view (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMeta; } public abstract interface class space/kscience/dataforge/meta/MutableTypedMeta : space/kscience/dataforge/meta/MutableMeta, space/kscience/dataforge/meta/TypedMeta { @@ -625,9 +627,9 @@ public final class space/kscience/dataforge/meta/SchemeKt { public static final fun listOfScheme (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; public static synthetic fun listOfScheme$default (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; public static synthetic fun listOfScheme$default (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun scheme (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; + public static final fun scheme (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; public static final fun scheme (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun scheme$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static synthetic fun scheme$default (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; public static synthetic fun scheme$default (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; public static final fun schemeOrNull (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; public static final fun schemeOrNull (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt index fd953085..bb9b90f6 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt @@ -249,11 +249,26 @@ public inline fun <reified E : Enum<E>> Meta?.enum(): E? = this?.value?.let { public val Meta?.stringList: List<String>? get() = this?.value?.list?.map { it.string } /** - * Create a provider that uses given provider for default values if those are not found in this provider + * Create a provider that uses a given provider for default values if those are not found in this provider */ -public fun Meta.withDefault(default: MetaProvider?): Meta = if (default == null) { +public fun MetaProvider.withDefault(default: MetaProvider?): Meta = if (default == null && this is Meta) { this } else { - //TODO optimize - toMutableMeta().withDefault(default) + object : Meta { + override val value: Value? + get() = this@withDefault.getValue(Name.EMPTY) ?: default?.getValue(Name.EMPTY) + + override val items: Map<NameToken, Meta> + get() = buildMap { + default?.get(Name.EMPTY)?.items?.let { putAll(it) } + this@withDefault.get(Name.EMPTY)?.items?.let { putAll(it) } + } + + override fun toString(): String = Meta.toString(this) + + override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta) + + override fun hashCode(): Int = Meta.hashCode(this) + + } } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt index 274671bf..f1084692 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt @@ -37,7 +37,7 @@ public interface MutableMeta : Meta, MutableMetaProvider { override val items: Map<NameToken, MutableMeta> /** - * Get or set value of this node + * Get or set the value of this node */ override var value: Value? @@ -437,8 +437,8 @@ public inline fun Meta.copy(modification: MutableMeta.() -> Unit = {}): Meta = M } private class MutableMetaWithDefault( - val source: MutableMeta, val default: MetaProvider, val rootName: Name, -) : MutableMeta by source { + val source: MutableMetaProvider, val default: MetaProvider, val rootName: Name, +) : MutableMeta { override val items: Map<NameToken, MutableMeta> get() { val sourceKeys: Collection<NameToken> = source[rootName]?.items?.keys ?: emptyList() @@ -457,15 +457,37 @@ private class MutableMetaWithDefault( override fun get(name: Name): MutableMeta = MutableMetaWithDefault(source, default, rootName + name) + override fun set(name: Name, node: Meta?) { + source[name] = node + } + + override fun setValue(name: Name, value: Value?) { + source.setValue(name, value) + } + + override fun getOrCreate(name: Name): MutableMeta = + MutableMetaWithDefault(source, default, rootName + name) + override fun toString(): String = Meta.toString(this) override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta) override fun hashCode(): Int = Meta.hashCode(this) } /** - * Create a mutable item provider that uses given provider for default values if those are not found in this provider. + * Create a mutable item provider that uses a given provider for default values if those are not found in this provider. * Changes are propagated only to this provider. */ -public fun MutableMeta.withDefault(default: MetaProvider?): MutableMeta = if (default == null) { - this -} else MutableMetaWithDefault(this, default, Name.EMPTY) +public fun MutableMetaProvider.withDefault( + default: MetaProvider? +): MutableMeta = if (default == null) { + (this as? MutableMeta) ?: asMutableMeta() +} else { + MutableMetaWithDefault(this, default, Name.EMPTY) +} + +/** + * Use multiple default providers. Providers are queried in order of appearance. The first non-null value is returned + */ +public fun MutableMetaProvider.withDefaults(vararg defaults: MetaProvider): MutableMeta = withDefault { name -> + defaults.firstNotNullOfOrNull { it[name] } +} \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaView.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaView.kt index 2bc3f9aa..81a87ef9 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaView.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaView.kt @@ -9,7 +9,7 @@ import space.kscience.dataforge.names.plus * A [Meta] child proxy that creates required nodes on value write */ private class MutableMetaView( - val origin: MutableMeta, + val origin: MutableMetaProvider, val path: Name ) : MutableMeta { @@ -42,6 +42,11 @@ private class MutableMetaView( * The difference between this method and regular [getOrCreate] is that [getOrCreate] always creates and attaches node * even if it is empty. */ -public fun MutableMeta.view(name: Name): MutableMeta = MutableMetaView(this, name) +public fun MutableMetaProvider.view(name: Name): MutableMeta = MutableMetaView(this, name) -public fun MutableMeta.view(name: String): MutableMeta = view(name.parseAsName()) \ No newline at end of file +public fun MutableMetaProvider.view(name: String): MutableMeta = view(name.parseAsName()) + +/** + * Create a view of root node, thus effectively representing [MutableMetaProvider] as [MutableMeta] + */ +public fun MutableMetaProvider.asMutableMeta(): MutableMeta = view(Name.EMPTY) \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt index 12eb4c68..1a5d2b0d 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt @@ -219,15 +219,15 @@ public fun <T : Scheme> Configurable.updateWith( /** - * A delegate that uses a [MetaReader] to wrap a child of this provider + * A delegate that uses a [SchemeSpec] to wrap a child of this provider */ -public fun <T : Scheme> MutableMetaProvider.scheme( +public fun <T : Scheme> MutableMeta.scheme( spec: SchemeSpec<T>, key: Name? = null, ): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> { override fun getValue(thisRef: Any?, property: KProperty<*>): T { val name = key ?: property.name.asName() - val node = get(name) ?: MutableMeta().also { set(name, it) } + val node = getOrCreate(name) return spec.write(node) } @@ -243,7 +243,7 @@ public fun <T : Scheme> Scheme.scheme( ): ReadWriteProperty<Any?, T> = meta.scheme(spec, key) /** - * A delegate that uses a [MetaReader] to wrap a child of this provider. + * A delegate that uses a [SchemeSpec] to wrap a child of this provider. * Returns null if meta with given name does not exist. */ public fun <T : Scheme> MutableMeta.schemeOrNull( diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt index 0c87bcc2..e84d7da0 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt @@ -6,7 +6,7 @@ package space.kscience.dataforge.meta public fun Value.isNull(): Boolean = this == Null /** - * Check if value is list. + * Check if the value is a list. */ public fun Value.isList(): Boolean = this.type == ValueType.LIST diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MutableMetaTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MutableMetaTest.kt index da4474d8..c286486a 100644 --- a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MutableMetaTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MutableMetaTest.kt @@ -3,9 +3,9 @@ package space.kscience.dataforge.meta import kotlin.test.Test import kotlin.test.assertEquals -class MutableMetaTest{ +class MutableMetaTest { @Test - fun remove(){ + fun remove() { val meta = MutableMeta { "aNode" put { "innerNode" put { @@ -19,4 +19,26 @@ class MutableMetaTest{ meta.remove("aNode.c") assertEquals(meta["aNode.c"], null) } + + @Test + fun withDefault() { + val metaWithDefault = MutableMeta().withDefault( + Meta { + "a" put { + value = 22.asValue() + "b" put true + } + } + ) + + assertEquals(22, metaWithDefault["a"].int) + assertEquals(true, metaWithDefault.getValue("a.b")?.boolean) + + metaWithDefault["a.b"] = "false" + + assertEquals(false, metaWithDefault["a.b"]?.boolean) + assertEquals(false, metaWithDefault.getValue("a.b")?.boolean) + assertEquals(22, metaWithDefault.getValue("a")?.int) + + } } \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SpecificationTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SpecificationTest.kt index dc9b9d64..442fe188 100644 --- a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SpecificationTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SpecificationTest.kt @@ -26,18 +26,17 @@ internal class TestScheme : Scheme() { companion object : SchemeSpec<TestScheme>(::TestScheme) } -private class SchemeWithInit: Scheme(){ +private class SchemeWithInit : Scheme() { init { set("initial", "initialValue") } var initial by string() - companion object: SchemeSpec<SchemeWithInit>(::SchemeWithInit) + companion object : SchemeSpec<SchemeWithInit>(::SchemeWithInit) } - class SpecificationTest { // @Test @@ -126,11 +125,11 @@ class SpecificationTest { } @Test - fun testListSubscription(){ + fun testListSubscription() { val scheme = TestScheme.empty() var value: Value? = null - scheme.v = ListValue(0.0,0.0,0.0) - scheme.useProperty(TestScheme::v){ + scheme.v = ListValue(0.0, 0.0, 0.0) + scheme.useProperty(TestScheme::v) { value = it } scheme.v = ListValue(1.0, 2.0, 3.0) @@ -138,17 +137,17 @@ class SpecificationTest { } @Test - fun testSubScheme(){ + fun testSubScheme() { val scheme = TestScheme.empty() scheme.sub.subValue = "aaa" - assertEquals("aaa",scheme.sub.subValue) + assertEquals("aaa", scheme.sub.subValue) } @Test - fun testSchemeWithInit(){ + fun testSchemeWithInit() { val scheme = SchemeWithInit() assertEquals("initialValue", scheme.initial) scheme.initial = "none" diff --git a/gradle.properties b/gradle.properties index 015d2c52..44c3306a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,6 @@ org.gradle.jvmargs=-Xmx4096m kotlin.mpp.stability.nowarn=true kotlin.native.ignoreDisabledTargets=true org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled +kotlin.native.enableKlibsCrossCompilation=true toolsVersion=0.16.1-kotlin-2.1.0 \ No newline at end of file