From 69ec7c4daca92c51eea678969b053bc91df4b568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Petu=C5=A1ka?= Date: Sat, 27 Nov 2021 18:38:02 +0000 Subject: [PATCH] Kotlin 1.6.0 (#16) * 1.6.0 --- .gitattributes | 2 + README.md | 318 +++++++++--------- buildSrc/build.gradle.kts | 44 +-- buildSrc/gradle.properties | 10 +- buildSrc/settings.gradle.kts | 26 +- .../src/main/kotlin/plugin.common.gradle.kts | 90 ++--- .../main/kotlin/plugin.git-hooks.gradle.kts | 6 +- .../main/kotlin/plugin.library-mpp.gradle.kts | 292 ++++++++-------- .../kotlin/plugin.publishing-mpp.gradle.kts | 162 ++++----- .../kotlin/plugin.publishing-nexus.gradle.kts | 30 +- .../main/kotlin/plugin.publishing.gradle.kts | 252 +++++++------- .../main/kotlin/util/KotlinTargetDetails.kt | 134 ++++---- buildSrc/src/main/kotlin/util/gradle.kt | 84 ++--- .../src/main/kotlin/util/nativeTargetGroup.kt | 56 +-- gradle.properties | 56 +-- .../src/commonMain/kotlin/assertKlip.kt | 74 ++-- library/klip-api/src/main/AndroidManifest.xml | 2 +- .../klip-core/src/androidMain/kotlin/File.kt | 42 +-- .../src/commonMain/kotlin/Klippable.kt | 56 +-- .../src/commonMain/kotlin/ext/File.kt | 126 +++---- .../src/commonMain/kotlin/ext/cleanupPath.kt | 6 +- .../src/commonMain/kotlin/int/KlipContext.kt | 28 +- .../src/commonMain/kotlin/int/KlipManager.kt | 292 ++++++++-------- .../src/commonMain/kotlin/int/KlipType.kt | 10 +- .../src/commonTest/kotlin/ext/FileTest.kt | 120 +++---- .../klip-core/src/fallbackMain/kotlin/File.kt | 134 ++++---- library/klip-core/src/jsMain/kotlin/File.kt | 174 +++++----- library/klip-core/src/jsMain/kotlin/util.kt | 6 +- library/klip-core/src/jvmMain/kotlin/File.kt | 42 +-- .../klip-core/src/main/AndroidManifest.xml | 2 +- .../klip-core/src/mingwMain/kotlin/File.kt | 40 +-- library/klip-core/src/unixMain/kotlin/File.kt | 42 +-- .../src/main/kotlin/KlipPlugin.kt | 2 +- .../src/main/kotlin/util/KlipOption.kt | 16 +- .../src/main/kotlin/KlipComponentRegistrar.kt | 2 + .../src/main/kotlin/util/Debugger.kt | 23 ++ sandbox/build.gradle.kts | 21 +- sandbox/settings.gradle.kts | 35 +- .../kotest/__klips__/JvmBehaviourSpec.kt.klip | 6 + .../kotest/__klips__/JvmDescribeSpec.kt.klip | 6 + .../kotest/__klips__/JvmExpectSpec.kt.klip | 6 + .../kotest/__klips__/JvmFeatureSpec.kt.klip | 6 + .../kotest/__klips__/JvmFreeSpec.kt.klip | 6 + .../kotest/__klips__/JvmWordSpec.kt.klip | 6 + src/main/AndroidManifest.xml | 2 +- versions.properties | 4 +- 46 files changed, 1488 insertions(+), 1411 deletions(-) create mode 100644 plugin/klip-kotlin-plugin/src/main/kotlin/util/Debugger.kt diff --git a/.gitattributes b/.gitattributes index 022b8414..0cf6ebd3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,4 +2,6 @@ # https://help.github.com/articles/dealing-with-line-endings/ # # These are explicitly windows files and should use crlf +*.kt text eol=lf +*.kts text eol=lf *.bat text eol=crlf diff --git a/README.md b/README.md index 2f65aca0..3e01884e 100644 --- a/README.md +++ b/README.md @@ -1,159 +1,159 @@ -[![Gitpod ready-to-code](https://img.shields.io/badge/gitpod-ready_to_code-blue?logo=gitpod&style=flat-square)](https://gitpod.io/#https://github.com/mpetuska/klip) -[![Slack chat](https://img.shields.io/badge/kotlinlang-chat-green?logo=slack&style=flat-square)](https://kotlinlang.slack.com/team/UL1A5BA2X) -[![Dokka docs](https://img.shields.io/badge/docs-dokka-orange?style=flat-square)](http://mpetuska.github.io/klip) -[![Version gradle-plugin-portal](https://img.shields.io/maven-metadata/v?label=gradle%20plugin%20portal&style=flat-square&logo=gradle&metadataUrl=https%3A%2F%2Fplugins.gradle.org%2Fm2%2Fdev.petuska%2Fklip-gradle-plugin%2Fmaven-metadata.xml)](https://plugins.gradle.org/plugin/dev.petuska.klip) -[![Version maven-central](https://img.shields.io/maven-central/v/dev.petuska/klip?logo=apache-maven&style=flat-square)](https://mvnrepository.com/artifact/dev.petuska/klip/latest) - -# KLIP -Kotlin Multiplatform snapshot ((c|k)lip) manager for tests. Automatically generates and asserts against a -persistent `Any::toString()` representation of the object until you explicitly trigger an update. Powered by kotlin -compiler plugin to inject relevant keys and paths. - -# Support -The plugin only works on targets using new IR kotlin compiler (which is pretty much all of them since kotlin 1.5 except -JS which still defaults to legacy compiler). - -# Versions -The current version was built using the following tooling versions and is guaranteed to work with this setup. Given the -experimental nature of kotlin compiler plugin API, the plugin powering this library is likely to stop working on -projects using newer/older kotlin versions. -* Kotlin: `1.6.0` -* Gradle: `7.2.0` -* JDK: `11` - -# Targets -Bellow is a list of currently supported targets and planned targets: -- [x] android -- [x] js -- [x] jvm -- [x] linuxX64 -- [x] mingwX64 -- [x] macosX64 -- [x] macosArm64 -- [x] iosArm32 -- [x] iosArm64 -- [x] iosSimulatorArm64 -- [x] iosX64 -- [x] watchosX86 -- [x] watchosX64 -- [x] watchosArm64 -- [x] watchosSimulatorArm64 -- [x] watchosArm32 -- [x] tvosArm64 -- [x] tvosSimulatorArm64 -- [x] tvosX64 - -There's also a subset of targets that you currently cannot run tests on (and as such making the library redundant). -These targets will use a fallback implementation that throws an error on native api access (since those targets will not -execute tests) to enable the general library usage in `commonMain` source set. If you have a valid use-case of the -library for these targets, please raise an issue to discuss a real implementation. -- [x] androidNativeArm32 -- [x] androidNativeArm64 -- [x] linuxArm32Hfp -- [x] linuxMips32 -- [x] linuxMipsel32 -- [x] linuxArm64 -- [x] mingwX86 - -# Usage -1. Apply the plugin and add a runtime dependency. -```kotlin -plugins { - kotlin("multiplatform") - id("dev.petuska.klip") version "<>" - - kotlin { - sourceSets { - commonTest { - dependencies { - implementation("dev.petuska:klip:<>") - } - } - } - } -} -``` -2. (Optional) Configure the plugin extension (shown with default values). For property descriptions. - see [Gradle Properties](#gradle-properties) -```kotlin -klip { - enabled = true // Turns the compiler plugin on/off - update = false // Whether to overwrite the existing klips while running tests - klipAnnotations = setOf("dev.petuska.klip.core.Klippable") // Takes full control of annotations - klipAnnotation("dev.petuska.klip.core.Klippable") // Appends the annotation to the default ones - scopeAnnotations = setOf( - // Takes full control of annotations - "kotlin.test.Test", - "org.junit.Test", - "org.junit.jupiter.api.Test", - "org.testng.annotations.Test", - "io.kotest.core.spec.style.AnnotationSpec.Test", - ) - scopeAnnotation("kotlin.test.Test") // Appends the annotation to the default ones - scopeFunctions = setOf( - // Takes full control of functions - "io.kotest.core.spec.style.scopes.FunSpecRootContext.test", - "io.kotest.core.spec.style.scopes.DescribeSpecContainerContext.it", - "io.kotest.core.spec.style.scopes.BehaviorSpecWhenContainerContext.Then", - "io.kotest.core.spec.style.scopes.BehaviorSpecWhenContainerContext.then", - "io.kotest.core.spec.style.scopes.WordSpecShouldContainerContext.invoke", - "io.kotest.core.spec.style.scopes.FreeSpecContainerContext.invoke", - "io.kotest.core.spec.style.scopes.FeatureSpecContainerContext.scenario", - "io.kotest.core.spec.style.scopes.ExpectSpecContainerContext.expect", - ) - scopeFunction("io.kotest.core.spec.style.scopes.FunSpecRootContext.test") // Appends the function to the default ones -} -``` -3. Use provided klip assertions anywhere under one of the `scopeAnnotations` or `scopeFunctions`. -```kotlin -class MyTest { - data class DomainObject(val name: String, val value: String?) - - @Test - fun test1() { - assertMatchesKlip(DomainObject("Dick", "Dickens")) - DomainObject("John", "Doe").assertKlip() - } - - @Test - fun test2() { - doAssertions() - } - - private fun doAssertions() { - assertMatchesKlip(DomainObject("Joe", "Mama")) - DomainObject("Ben", "Dover").assertKlip() - } -} -``` - -## Gradle Properties -Most of the DSL configuration options can also be set/overridden via gradle properties -`./gradlew -Pprop.name=propValue`, `gradle.properties` or `~/.gradle/gradle.properties`. Environment -variables are also supported, however gradle properties take precedence over them. Bellow is the full list of supported -properties: -* `klip.enabled (KLIP_ENABLED)` - toggles the compiler processing on/off. -* `klip.update (KLIP_UPDATE)` - if true, will override and update all previous klips during test run. -* `klip.klipAnnotations (KLIP_KLIPANNOTATIONS)` - comma separated list of fully qualified names of annotations to - process. Only useful when writing your own klippable functions. -* `klip.scopeAnnotations (KLIP_SCOPEANNOTATIONS` - comma separated list of fully qualified names of test annotations to - scope klip keys. -* `klip.scopeFunctions (KLIP_SCOPEFUNCTIONS` - comma separated list of fully qualified names of test functions to scope - klip keys. - -## Basic Flow -1. Run tests as normal and use generated klip assertions such as `assertMatchesKlip(myObject)` - or `myObject.assertKlip()`. New klips will always be written to file, whereas existing ones (identified by test class - scope and given id) will be read and used for assertions. -2. When the actual value changes, tests will fail since they do not match previous klip. In such cases inspect the - differences and if everything is as expected, re-run test(s) with klip updates enabled. This is done by either - passing a gradle prop `./gradlew test -Pklip.update`, - setting an environment variable `KLIP_UPDATE=true ./gradlew test` or running update task `./gradlew klipUpdate`. - -# Modules -* `:library:klip-core` - main runtime library -* `:library:klip-api` - assertion api and utility DSLs -* `:plugin:klip-gradle-plugin` - gradle plugin to manage kotlin compiler plugins -* `:plugin:klip-kotlin-plugin` - kotlin compiler plugin for jvm & js that does the actual work -* `:plugin:klip-kotlin-plugin:klip-kotlin-plugin-native` - kotlin compiler plugin for native that does the actual work -* `sandbox` - a playground to test local changes from consumer end +[![Gitpod ready-to-code](https://img.shields.io/badge/gitpod-ready_to_code-blue?logo=gitpod&style=flat-square)](https://gitpod.io/#https://github.com/mpetuska/klip) +[![Slack chat](https://img.shields.io/badge/kotlinlang-chat-green?logo=slack&style=flat-square)](https://kotlinlang.slack.com/team/UL1A5BA2X) +[![Dokka docs](https://img.shields.io/badge/docs-dokka-orange?style=flat-square)](http://mpetuska.github.io/klip) +[![Version gradle-plugin-portal](https://img.shields.io/maven-metadata/v?label=gradle%20plugin%20portal&style=flat-square&logo=gradle&metadataUrl=https%3A%2F%2Fplugins.gradle.org%2Fm2%2Fdev.petuska%2Fklip-gradle-plugin%2Fmaven-metadata.xml)](https://plugins.gradle.org/plugin/dev.petuska.klip) +[![Version maven-central](https://img.shields.io/maven-central/v/dev.petuska/klip?logo=apache-maven&style=flat-square)](https://mvnrepository.com/artifact/dev.petuska/klip/latest) + +# KLIP +Kotlin Multiplatform snapshot ((c|k)lip) manager for tests. Automatically generates and asserts against a +persistent `Any::toString()` representation of the object until you explicitly trigger an update. Powered by kotlin +compiler plugin to inject relevant keys and paths. + +# Support +The plugin only works on targets using new IR kotlin compiler (which is pretty much all of them since kotlin 1.5 except +JS which still defaults to legacy compiler). + +# Versions +The current version was built using the following tooling versions and is guaranteed to work with this setup. Given the +experimental nature of kotlin compiler plugin API, the plugin powering this library is likely to stop working on +projects using newer/older kotlin versions. +* Kotlin: `1.6.0` +* Gradle: `7.2.0` +* JDK: `11` + +# Targets +Bellow is a list of currently supported targets and planned targets: +- [x] android +- [x] js +- [x] jvm +- [x] linuxX64 +- [x] mingwX64 +- [x] macosX64 +- [x] macosArm64 +- [x] iosArm32 +- [x] iosArm64 +- [x] iosSimulatorArm64 +- [x] iosX64 +- [x] watchosX86 +- [x] watchosX64 +- [x] watchosArm64 +- [x] watchosSimulatorArm64 +- [x] watchosArm32 +- [x] tvosArm64 +- [x] tvosSimulatorArm64 +- [x] tvosX64 + +There's also a subset of targets that you currently cannot run tests on (and as such making the library redundant). +These targets will use a fallback implementation that throws an error on native api access (since those targets will not +execute tests) to enable the general library usage in `commonMain` source set. If you have a valid use-case of the +library for these targets, please raise an issue to discuss a real implementation. +- [x] androidNativeArm32 +- [x] androidNativeArm64 +- [x] linuxArm32Hfp +- [x] linuxMips32 +- [x] linuxMipsel32 +- [x] linuxArm64 +- [x] mingwX86 + +# Usage +1. Apply the plugin and add a runtime dependency. +```kotlin +plugins { + kotlin("multiplatform") + id("dev.petuska.klip") version "<>" + + kotlin { + sourceSets { + commonTest { + dependencies { + implementation("dev.petuska:klip:<>") + } + } + } + } +} +``` +2. (Optional) Configure the plugin extension (shown with default values). For property descriptions. + see [Gradle Properties](#gradle-properties) +```kotlin +klip { + enabled = true // Turns the compiler plugin on/off + update = false // Whether to overwrite the existing klips while running tests + klipAnnotations = setOf("dev.petuska.klip.core.Klippable") // Takes full control of annotations + klipAnnotation("dev.petuska.klip.core.Klippable") // Appends the annotation to the default ones + scopeAnnotations = setOf( + // Takes full control of annotations + "kotlin.test.Test", + "org.junit.Test", + "org.junit.jupiter.api.Test", + "org.testng.annotations.Test", + "io.kotest.core.spec.style.AnnotationSpec.Test", + ) + scopeAnnotation("kotlin.test.Test") // Appends the annotation to the default ones + scopeFunctions = setOf( + // Takes full control of functions + "io.kotest.core.spec.style.scopes.FunSpecRootScope.test", + "io.kotest.core.spec.style.scopes.DescribeSpecContainerScope.it", + "io.kotest.core.spec.style.scopes.BehaviorSpecWhenContainerScope.Then", + "io.kotest.core.spec.style.scopes.BehaviorSpecWhenContainerScope.then", + "io.kotest.core.spec.style.scopes.WordSpecShouldContainerScope.invoke", + "io.kotest.core.spec.style.scopes.FreeSpecContainerScope.invoke", + "io.kotest.core.spec.style.scopes.FeatureSpecContainerScope.scenario", + "io.kotest.core.spec.style.scopes.ExpectSpecContainerScope.expect", + ) + scopeFunction("io.kotest.core.spec.style.scopes.FunSpecRootContext.test") // Appends the function to the default ones +} +``` +3. Use provided klip assertions anywhere under one of the `scopeAnnotations` or `scopeFunctions`. +```kotlin +class MyTest { + data class DomainObject(val name: String, val value: String?) + + @Test + fun test1() { + assertMatchesKlip(DomainObject("Dick", "Dickens")) + DomainObject("John", "Doe").assertKlip() + } + + @Test + fun test2() { + doAssertions() + } + + private fun doAssertions() { + assertMatchesKlip(DomainObject("Joe", "Mama")) + DomainObject("Ben", "Dover").assertKlip() + } +} +``` + +## Gradle Properties +Most of the DSL configuration options can also be set/overridden via gradle properties +`./gradlew -Pprop.name=propValue`, `gradle.properties` or `~/.gradle/gradle.properties`. Environment +variables are also supported, however gradle properties take precedence over them. Bellow is the full list of supported +properties: +* `klip.enabled (KLIP_ENABLED)` - toggles the compiler processing on/off. +* `klip.update (KLIP_UPDATE)` - if true, will override and update all previous klips during test run. +* `klip.klipAnnotations (KLIP_KLIPANNOTATIONS)` - comma separated list of fully qualified names of annotations to + process. Only useful when writing your own klippable functions. +* `klip.scopeAnnotations (KLIP_SCOPEANNOTATIONS` - comma separated list of fully qualified names of test annotations to + scope klip keys. +* `klip.scopeFunctions (KLIP_SCOPEFUNCTIONS` - comma separated list of fully qualified names of test functions to scope + klip keys. + +## Basic Flow +1. Run tests as normal and use generated klip assertions such as `assertMatchesKlip(myObject)` + or `myObject.assertKlip()`. New klips will always be written to file, whereas existing ones (identified by test class + scope and given id) will be read and used for assertions. +2. When the actual value changes, tests will fail since they do not match previous klip. In such cases inspect the + differences and if everything is as expected, re-run test(s) with klip updates enabled. This is done by either + passing a gradle prop `./gradlew test -Pklip.update`, + setting an environment variable `KLIP_UPDATE=true ./gradlew test` or running update task `./gradlew klipUpdate`. + +# Modules +* `:library:klip-core` - main runtime library +* `:library:klip-api` - assertion api and utility DSLs +* `:plugin:klip-gradle-plugin` - gradle plugin to manage kotlin compiler plugins +* `:plugin:klip-kotlin-plugin` - kotlin compiler plugin for jvm & js that does the actual work +* `:plugin:klip-kotlin-plugin:klip-kotlin-plugin-native` - kotlin compiler plugin for native that does the actual work +* `sandbox` - a playground to test local changes from consumer end diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 466bff35..17498b8f 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,22 +1,22 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -plugins { `kotlin-dsl` } - -repositories { - mavenCentral() - google() -} - -dependencies { - // implementation("io.kotest:kotest-framework-multiplatform-plugin-gradle:_") - implementation("com.android.tools.build:gradle:_") - implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:_") - implementation("org.jetbrains.dokka:dokka-gradle-plugin:_") - implementation("org.jetbrains.kotlin:kotlin-serialization:_") - implementation("com.diffplug.spotless:spotless-plugin-gradle:_") - implementation("io.github.gradle-nexus:publish-plugin:_") - implementation("com.github.gmazzo:gradle-buildconfig-plugin:_") - implementation("com.github.jakemarsden:git-hooks-gradle-plugin:_") -} - -tasks { withType { kotlinOptions.jvmTarget = "11" } } +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { `kotlin-dsl` } + +repositories { + mavenCentral() + google() +} + +dependencies { + implementation("io.kotest:kotest-framework-multiplatform-plugin-gradle:_") + implementation("com.android.tools.build:gradle:_") + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:_") + implementation("org.jetbrains.dokka:dokka-gradle-plugin:_") + implementation("org.jetbrains.kotlin:kotlin-serialization:_") + implementation("com.diffplug.spotless:spotless-plugin-gradle:_") + implementation("io.github.gradle-nexus:publish-plugin:_") + implementation("com.github.gmazzo:gradle-buildconfig-plugin:_") + implementation("com.github.jakemarsden:git-hooks-gradle-plugin:_") +} + +tasks { withType { kotlinOptions.jvmTarget = "11" } } diff --git a/buildSrc/gradle.properties b/buildSrc/gradle.properties index c2236e27..c01c9dba 100644 --- a/buildSrc/gradle.properties +++ b/buildSrc/gradle.properties @@ -1,5 +1,5 @@ -org.gradle.project.targetCompatibility=11 -org.gradle.vfs.watch=true -kotlin.style=official -gradle.cache=true -gradle.parallel=true +org.gradle.project.targetCompatibility=11 +org.gradle.vfs.watch=true +kotlin.style=official +gradle.cache=true +gradle.parallel=true diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index 8a152153..d61f7fc0 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -1,13 +1,13 @@ -pluginManagement { - repositories { - gradlePluginPortal() - mavenCentral() - } - plugins { - id("de.fayard.refreshVersions") version "0.23.0" - } -} - -plugins { - id("de.fayard.refreshVersions") -} +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + } + plugins { + id("de.fayard.refreshVersions") version "0.23.0" + } +} + +plugins { + id("de.fayard.refreshVersions") +} diff --git a/buildSrc/src/main/kotlin/plugin.common.gradle.kts b/buildSrc/src/main/kotlin/plugin.common.gradle.kts index cf9b3ac0..594116e6 100644 --- a/buildSrc/src/main/kotlin/plugin.common.gradle.kts +++ b/buildSrc/src/main/kotlin/plugin.common.gradle.kts @@ -1,45 +1,45 @@ -import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.jetbrains.kotlin.gradle.tasks.KotlinTest - -plugins { - id("com.diffplug.spotless") - idea -} - -repositories { - mavenCentral() - google() -} - -idea { - module { - isDownloadSources = true - isDownloadJavadoc = true - } -} - -spotless { - kotlin { ktfmt() } - kotlinGradle { ktfmt() } -} - -tasks { - project.properties["org.gradle.project.targetCompatibility"]?.toString()?.let { - withType { kotlinOptions { jvmTarget = it } } - } - afterEvaluate { - if (tasks.findByName("compile") == null) { - register("compile") { - dependsOn(withType(AbstractKotlinCompile::class)) - group = "build" - } - } - if (tasks.findByName("allTests") == null) { - register("allTests") { - dependsOn(withType(KotlinTest::class)) - group = "verification" - } - } - } -} +import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.tasks.KotlinTest + +plugins { + id("com.diffplug.spotless") + idea +} + +repositories { + mavenCentral() + google() +} + +idea { + module { + isDownloadSources = true + isDownloadJavadoc = true + } +} + +spotless { + kotlin { ktfmt() } + kotlinGradle { ktfmt() } +} + +tasks { + project.properties["org.gradle.project.targetCompatibility"]?.toString()?.let { + withType { kotlinOptions { jvmTarget = it } } + } + afterEvaluate { + if (tasks.findByName("compile") == null) { + register("compile") { + dependsOn(withType(AbstractKotlinCompile::class)) + group = "build" + } + } + if (tasks.findByName("allTests") == null) { + register("allTests") { + dependsOn(withType(KotlinTest::class)) + group = "verification" + } + } + } +} diff --git a/buildSrc/src/main/kotlin/plugin.git-hooks.gradle.kts b/buildSrc/src/main/kotlin/plugin.git-hooks.gradle.kts index ef35ddad..e392ce8a 100644 --- a/buildSrc/src/main/kotlin/plugin.git-hooks.gradle.kts +++ b/buildSrc/src/main/kotlin/plugin.git-hooks.gradle.kts @@ -1,3 +1,3 @@ -plugins { id("com.github.jakemarsden.git-hooks") } - -gitHooks { setHooks(mapOf("pre-commit" to "spotlessApply", "pre-push" to "spotlessCheck")) } +plugins { id("com.github.jakemarsden.git-hooks") } + +gitHooks { setHooks(mapOf("pre-commit" to "spotlessApply", "pre-push" to "spotlessCheck")) } diff --git a/buildSrc/src/main/kotlin/plugin.library-mpp.gradle.kts b/buildSrc/src/main/kotlin/plugin.library-mpp.gradle.kts index 8cae8706..1c2812d9 100644 --- a/buildSrc/src/main/kotlin/plugin.library-mpp.gradle.kts +++ b/buildSrc/src/main/kotlin/plugin.library-mpp.gradle.kts @@ -1,146 +1,146 @@ -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget -import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile -import org.jetbrains.kotlin.gradle.tasks.CInteropProcess -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.jetbrains.kotlin.konan.target.Family -import org.jetbrains.kotlin.konan.target.HostManager -import util.CI -import util.SANDBOX -import util.buildHost - -plugins { - kotlin("multiplatform") - kotlin("plugin.serialization") - id("com.android.library") - id("plugin.common") -} - -android { - compileSdkVersion(31) - defaultConfig { - minSdkVersion(1) - targetSdkVersion(31) - } -} - -kotlin { - explicitApi() - - android() - jvm() - js { - useCommonJs() - nodejs() - } - macosX64() - macosArm64() - linuxX64() - mingwX64() - iosArm32() - iosArm64() - iosX64() - iosSimulatorArm64() - watchosX86() - watchosX64() - watchosArm64() - watchosArm32() - watchosSimulatorArm64() - tvosArm64() - tvosX64() - tvosSimulatorArm64() - - // Fallback Targets - val fallbackTargets = setOf( - androidNativeArm32(), - androidNativeArm64(), - mingwX86(), - linuxArm32Hfp(), - linuxMips32(), - linuxMipsel32(), - linuxArm64(), - ) - - sourceSets { - val commonMain by getting - val commonTest by getting { - dependencies { - implementation(kotlin("test")) - implementation(kotlin("test-annotations-common")) - } - } - val fallbackMain by creating { - dependsOn(commonMain) - } - val nativeMain by creating { - dependsOn(commonMain) - } - val mingwMain by creating { - dependsOn(nativeMain) - } - val unixMain by creating { - dependsOn(nativeMain) - } - val linuxMain by creating { - dependsOn(unixMain) - } - val appleMain by creating { - dependsOn(unixMain) - } - - val fallbackTest by creating { - dependsOn(commonTest) - } - val nativeTest by creating { - dependsOn(commonTest) - } - val mingwTest by creating { - dependsOn(nativeTest) - } - val unixTest by creating { - dependsOn(nativeTest) - } - val linuxTest by creating { - dependsOn(unixTest) - } - val appleTest by creating { - dependsOn(unixTest) - } - - targets.withType { - val main = compilations["main"].defaultSourceSet - val test = compilations["test"].defaultSourceSet - when { - this in fallbackTargets -> { - main.dependsOn(fallbackMain) - test.dependsOn(fallbackTest) - } - konanTarget.family == Family.MINGW -> { - main.dependsOn(mingwMain) - test.dependsOn(mingwTest) - } - konanTarget.family.isAppleFamily -> { - main.dependsOn(appleMain) - test.dependsOn(appleTest) - } - else -> { - main.dependsOn(linuxMain) - test.dependsOn(linuxTest) - } - } - } - } -} - -tasks { - withType { - onlyIf { - !CI || SANDBOX || konanTarget.buildHost == HostManager.host.family - } - } - withType> { - onlyIf { - !CI || SANDBOX || compilation.konanTarget.buildHost == HostManager.host.family - } - } - withType { kotlinOptions.jvmTarget = "11" } -} +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile +import org.jetbrains.kotlin.gradle.tasks.CInteropProcess +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.konan.target.Family +import org.jetbrains.kotlin.konan.target.HostManager +import util.CI +import util.SANDBOX +import util.buildHost + +plugins { + kotlin("multiplatform") + kotlin("plugin.serialization") + id("com.android.library") + id("plugin.common") +} + +android { + compileSdkVersion(31) + defaultConfig { + minSdkVersion(1) + targetSdkVersion(31) + } +} + +kotlin { + explicitApi() + + android() + jvm() + js { + useCommonJs() + nodejs() + } + macosX64() + macosArm64() + linuxX64() + mingwX64() + iosArm32() + iosArm64() + iosX64() + iosSimulatorArm64() + watchosX86() + watchosX64() + watchosArm64() + watchosArm32() + watchosSimulatorArm64() + tvosArm64() + tvosX64() + tvosSimulatorArm64() + + // Fallback Targets + val fallbackTargets = setOf( + androidNativeArm32(), + androidNativeArm64(), + mingwX86(), + linuxArm32Hfp(), + linuxMips32(), + linuxMipsel32(), + linuxArm64(), + ) + + sourceSets { + val commonMain by getting + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + implementation(kotlin("test-annotations-common")) + } + } + val fallbackMain by creating { + dependsOn(commonMain) + } + val nativeMain by creating { + dependsOn(commonMain) + } + val mingwMain by creating { + dependsOn(nativeMain) + } + val unixMain by creating { + dependsOn(nativeMain) + } + val linuxMain by creating { + dependsOn(unixMain) + } + val appleMain by creating { + dependsOn(unixMain) + } + + val fallbackTest by creating { + dependsOn(commonTest) + } + val nativeTest by creating { + dependsOn(commonTest) + } + val mingwTest by creating { + dependsOn(nativeTest) + } + val unixTest by creating { + dependsOn(nativeTest) + } + val linuxTest by creating { + dependsOn(unixTest) + } + val appleTest by creating { + dependsOn(unixTest) + } + + targets.withType { + val main = compilations["main"].defaultSourceSet + val test = compilations["test"].defaultSourceSet + when { + this in fallbackTargets -> { + main.dependsOn(fallbackMain) + test.dependsOn(fallbackTest) + } + konanTarget.family == Family.MINGW -> { + main.dependsOn(mingwMain) + test.dependsOn(mingwTest) + } + konanTarget.family.isAppleFamily -> { + main.dependsOn(appleMain) + test.dependsOn(appleTest) + } + else -> { + main.dependsOn(linuxMain) + test.dependsOn(linuxTest) + } + } + } + } +} + +tasks { + withType { + onlyIf { + !CI || SANDBOX || konanTarget.buildHost == HostManager.host.family + } + } + withType> { + onlyIf { + !CI || SANDBOX || compilation.konanTarget.buildHost == HostManager.host.family + } + } + withType { kotlinOptions.jvmTarget = "11" } +} diff --git a/buildSrc/src/main/kotlin/plugin.publishing-mpp.gradle.kts b/buildSrc/src/main/kotlin/plugin.publishing-mpp.gradle.kts index 1a601084..4c2f41b2 100644 --- a/buildSrc/src/main/kotlin/plugin.publishing-mpp.gradle.kts +++ b/buildSrc/src/main/kotlin/plugin.publishing-mpp.gradle.kts @@ -1,81 +1,81 @@ -import org.jetbrains.kotlin.gradle.plugin.KotlinTarget -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget -import org.jetbrains.kotlin.konan.target.Family -import org.jetbrains.kotlin.konan.target.HostManager -import util.CI -import util.SANDBOX -import util.buildHost -import util.isMainHost - -plugins { - kotlin("multiplatform") - id("plugin.publishing") -} - -kotlin { - fun Collection.onlyBuildIf(enabled: Spec) { - forEach { - it.compilations.all { - compileKotlinTask.onlyIf(enabled) - } - } - } - - fun Collection.onlyPublishIf(enabled: Spec) { - val publications: Collection = map { it.name } - afterEvaluate { - publishing { - publications { - matching { it.name in publications }.all { - val targetPublication = this@all - tasks.withType() - .matching { it.publication == targetPublication } - .configureEach { - onlyIf(enabled) - } - tasks.withType() - .matching { it.publication.get() == targetPublication } - .configureEach { - onlyIf(enabled) - } - } - } - } - } - } - - val nativeTargets = targets.withType() - val windowsHostTargets = nativeTargets.filter { it.konanTarget.buildHost == Family.MINGW } - val linuxHostTargets = nativeTargets.filter { it.konanTarget.buildHost == Family.LINUX } - val osxHostTargets = nativeTargets.filter { it.konanTarget.buildHost == Family.OSX } - val mainHostTargets = targets.filter { it !in nativeTargets } - val androidTargets = targets.withType() - logger.info("Linux host targets: $linuxHostTargets") - logger.info("OSX host targets: $osxHostTargets") - logger.info("Windows host targets: $windowsHostTargets") - logger.info("Main host targets: $mainHostTargets") - logger.info("Android targets: $androidTargets") - - androidTargets.forEach { - if (!CI || SANDBOX || isMainHost) { - it.publishLibraryVariants("release", "debug") - } - } - - linuxHostTargets.onlyBuildIf { !CI || SANDBOX || HostManager.hostIsLinux } - linuxHostTargets.onlyPublishIf { !CI || SANDBOX || HostManager.hostIsLinux } - - osxHostTargets.onlyBuildIf { !CI || SANDBOX || HostManager.hostIsMac } - osxHostTargets.onlyPublishIf { !CI || SANDBOX || HostManager.hostIsMac } - - windowsHostTargets.onlyBuildIf { !CI || SANDBOX || HostManager.hostIsMingw } - windowsHostTargets.onlyPublishIf { !CI || SANDBOX || HostManager.hostIsMingw } - - mainHostTargets.onlyBuildIf { - !CI || SANDBOX || isMainHost - } - (mainHostTargets + Named { "kotlinMultiplatform" }).onlyPublishIf { - !CI || SANDBOX || isMainHost - } -} +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.konan.target.Family +import org.jetbrains.kotlin.konan.target.HostManager +import util.CI +import util.SANDBOX +import util.buildHost +import util.isMainHost + +plugins { + kotlin("multiplatform") + id("plugin.publishing") +} + +kotlin { + fun Collection.onlyBuildIf(enabled: Spec) { + forEach { + it.compilations.all { + compileKotlinTask.onlyIf(enabled) + } + } + } + + fun Collection.onlyPublishIf(enabled: Spec) { + val publications: Collection = map { it.name } + afterEvaluate { + publishing { + publications { + matching { it.name in publications }.all { + val targetPublication = this@all + tasks.withType() + .matching { it.publication == targetPublication } + .configureEach { + onlyIf(enabled) + } + tasks.withType() + .matching { it.publication.get() == targetPublication } + .configureEach { + onlyIf(enabled) + } + } + } + } + } + } + + val nativeTargets = targets.withType() + val windowsHostTargets = nativeTargets.filter { it.konanTarget.buildHost == Family.MINGW } + val linuxHostTargets = nativeTargets.filter { it.konanTarget.buildHost == Family.LINUX } + val osxHostTargets = nativeTargets.filter { it.konanTarget.buildHost == Family.OSX } + val mainHostTargets = targets.filter { it !in nativeTargets } + val androidTargets = targets.withType() + logger.info("Linux host targets: $linuxHostTargets") + logger.info("OSX host targets: $osxHostTargets") + logger.info("Windows host targets: $windowsHostTargets") + logger.info("Main host targets: $mainHostTargets") + logger.info("Android targets: $androidTargets") + + androidTargets.forEach { + if (!CI || SANDBOX || isMainHost) { + it.publishLibraryVariants("release", "debug") + } + } + + linuxHostTargets.onlyBuildIf { !CI || SANDBOX || HostManager.hostIsLinux } + linuxHostTargets.onlyPublishIf { !CI || SANDBOX || HostManager.hostIsLinux } + + osxHostTargets.onlyBuildIf { !CI || SANDBOX || HostManager.hostIsMac } + osxHostTargets.onlyPublishIf { !CI || SANDBOX || HostManager.hostIsMac } + + windowsHostTargets.onlyBuildIf { !CI || SANDBOX || HostManager.hostIsMingw } + windowsHostTargets.onlyPublishIf { !CI || SANDBOX || HostManager.hostIsMingw } + + mainHostTargets.onlyBuildIf { + !CI || SANDBOX || isMainHost + } + (mainHostTargets + Named { "kotlinMultiplatform" }).onlyPublishIf { + !CI || SANDBOX || isMainHost + } +} diff --git a/buildSrc/src/main/kotlin/plugin.publishing-nexus.gradle.kts b/buildSrc/src/main/kotlin/plugin.publishing-nexus.gradle.kts index ae52cd6c..ae8b341c 100644 --- a/buildSrc/src/main/kotlin/plugin.publishing-nexus.gradle.kts +++ b/buildSrc/src/main/kotlin/plugin.publishing-nexus.gradle.kts @@ -1,15 +1,15 @@ -import util.by - -plugins { - id("plugin.common") - id("io.github.gradle-nexus.publish-plugin") -} - -nexusPublishing { - repositories { - sonatype { - nexusUrl by uri("https://s01.oss.sonatype.org/service/local/") - snapshotRepositoryUrl by uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") - } - } -} +import util.by + +plugins { + id("plugin.common") + id("io.github.gradle-nexus.publish-plugin") +} + +nexusPublishing { + repositories { + sonatype { + nexusUrl by uri("https://s01.oss.sonatype.org/service/local/") + snapshotRepositoryUrl by uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") + } + } +} diff --git a/buildSrc/src/main/kotlin/plugin.publishing.gradle.kts b/buildSrc/src/main/kotlin/plugin.publishing.gradle.kts index 1c86794c..270bb1ea 100644 --- a/buildSrc/src/main/kotlin/plugin.publishing.gradle.kts +++ b/buildSrc/src/main/kotlin/plugin.publishing.gradle.kts @@ -1,126 +1,126 @@ -import org.jetbrains.kotlin.gradle.plugin.KotlinTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.jetbrains.kotlin.konan.target.HostManager -import util.Git -import util.by - -plugins { - id("plugin.common") - id("org.jetbrains.dokka") - `maven-publish` - signing -} - -tasks { - register("javadocJar") { - dependsOn(dokkaHtml) - archiveClassifier.set("javadoc") - from(dokkaHtml.get().outputDirectory) - } - withType { - manifest { - attributes += - sortedMapOf( - "Built-By" to System.getProperty("user.name"), - "Build-Jdk" to System.getProperty("java.version"), - "Implementation-Version" to project.version, - "Created-By" to "${GradleVersion.current()}", - "Created-From" to "${Git.headCommitHash}") - } - } - val cleanMavenLocal by registering { - group = "build" - doLast { - val groupRepo = - file( - "${System.getProperty("user.home")}/.m2/repository/${project.group.toString().replace(".", "/")}") - publishing.publications.filterIsInstance().forEach { - groupRepo.resolve(it.artifactId).deleteRecursively() - } - } - } - named("clean") { dependsOn(cleanMavenLocal) } -} - -signing { - val signingKey: String? by project - val signingPassword: String? by project - if (signingKey != null) { - useInMemoryPgpKeys(signingKey, signingPassword) - sign(publishing.publications) - } -} - -val isMainHost = HostManager.simpleOsName().equals("${project.properties["project.mainOS"]}", true) - -tasks { withType { onlyIf } } - -publishing { - fun Collection.onlyBuildIf(enabled: Spec) { - forEach { it.compilations.all { compileKotlinTask.onlyIf(enabled) } } - } - - fun Collection.onlyPublishIf(enabled: Spec) { - val publications: Collection = map { it.name } - afterEvaluate { - publishing { - publications { - matching { it.name in publications }.all { - val targetPublication = this@all - tasks - .withType() - .matching { it.publication == targetPublication } - .configureEach { onlyIf(enabled) } - tasks - .withType() - .matching { it.publication.get() == targetPublication } - .configureEach { onlyIf(enabled) } - } - } - } - } - } - publications { - val ghOwnerId: String = project.properties["gh.owner.id"]!!.toString() - val ghOwnerName: String = project.properties["gh.owner.name"]!!.toString() - val ghOwnerEmail: String = project.properties["gh.owner.email"]!!.toString() - repositories { - maven("https://maven.pkg.github.com/$ghOwnerId/${rootProject.name}") { - name = "GitHub" - credentials { - username = System.getenv("GH_USERNAME") - password = System.getenv("GH_PASSWORD") - } - } - } - withType { - artifact(tasks["javadocJar"]) - pom { - name by project.name - url by "https://github.com/$ghOwnerId/${rootProject.name}" - description by project.description - - licenses { - license { - name by "The Apache License, Version 2.0" - url by "https://www.apache.org/licenses/LICENSE-2.0.txt" - } - } - - developers { - developer { - id by ghOwnerId - name by ghOwnerName - email by ghOwnerEmail - } - } - - scm { - connection by "scm:git:git@github.com:$ghOwnerId/${rootProject.name}.git" - url by "https://github.com/$ghOwnerId/${rootProject.name}" - tag by Git.headCommitHash - } - } - } - } -} +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.konan.target.HostManager +import util.Git +import util.by + +plugins { + id("plugin.common") + id("org.jetbrains.dokka") + `maven-publish` + signing +} + +tasks { + register("javadocJar") { + dependsOn(dokkaHtml) + archiveClassifier.set("javadoc") + from(dokkaHtml.get().outputDirectory) + } + withType { + manifest { + attributes += + sortedMapOf( + "Built-By" to System.getProperty("user.name"), + "Build-Jdk" to System.getProperty("java.version"), + "Implementation-Version" to project.version, + "Created-By" to "${GradleVersion.current()}", + "Created-From" to "${Git.headCommitHash}") + } + } + val cleanMavenLocal by registering { + group = "build" + doLast { + val groupRepo = + file( + "${System.getProperty("user.home")}/.m2/repository/${project.group.toString().replace(".", "/")}") + publishing.publications.filterIsInstance().forEach { + groupRepo.resolve(it.artifactId).deleteRecursively() + } + } + } + named("clean") { dependsOn(cleanMavenLocal) } +} + +signing { + val signingKey: String? by project + val signingPassword: String? by project + if (signingKey != null) { + useInMemoryPgpKeys(signingKey, signingPassword) + sign(publishing.publications) + } +} + +val isMainHost = HostManager.simpleOsName().equals("${project.properties["project.mainOS"]}", true) + +tasks { withType { onlyIf } } + +publishing { + fun Collection.onlyBuildIf(enabled: Spec) { + forEach { it.compilations.all { compileKotlinTask.onlyIf(enabled) } } + } + + fun Collection.onlyPublishIf(enabled: Spec) { + val publications: Collection = map { it.name } + afterEvaluate { + publishing { + publications { + matching { it.name in publications }.all { + val targetPublication = this@all + tasks + .withType() + .matching { it.publication == targetPublication } + .configureEach { onlyIf(enabled) } + tasks + .withType() + .matching { it.publication.get() == targetPublication } + .configureEach { onlyIf(enabled) } + } + } + } + } + } + publications { + val ghOwnerId: String = project.properties["gh.owner.id"]!!.toString() + val ghOwnerName: String = project.properties["gh.owner.name"]!!.toString() + val ghOwnerEmail: String = project.properties["gh.owner.email"]!!.toString() + repositories { + maven("https://maven.pkg.github.com/$ghOwnerId/${rootProject.name}") { + name = "GitHub" + credentials { + username = System.getenv("GH_USERNAME") + password = System.getenv("GH_PASSWORD") + } + } + } + withType { + artifact(tasks["javadocJar"]) + pom { + name by project.name + url by "https://github.com/$ghOwnerId/${rootProject.name}" + description by project.description + + licenses { + license { + name by "The Apache License, Version 2.0" + url by "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + + developers { + developer { + id by ghOwnerId + name by ghOwnerName + email by ghOwnerEmail + } + } + + scm { + connection by "scm:git:git@github.com:$ghOwnerId/${rootProject.name}.git" + url by "https://github.com/$ghOwnerId/${rootProject.name}" + tag by Git.headCommitHash + } + } + } + } +} diff --git a/buildSrc/src/main/kotlin/util/KotlinTargetDetails.kt b/buildSrc/src/main/kotlin/util/KotlinTargetDetails.kt index f9196d82..a80e731b 100644 --- a/buildSrc/src/main/kotlin/util/KotlinTargetDetails.kt +++ b/buildSrc/src/main/kotlin/util/KotlinTargetDetails.kt @@ -1,67 +1,67 @@ -package util - -import org.jetbrains.kotlin.konan.target.Family -import org.jetbrains.kotlin.konan.target.KonanTarget - -enum class KotlinTargetDetails( - val presetName: String, - val hasCoroutines: Boolean, -) { - JVM("jvm", true), - ANDROID("android", true), - JS("jsIr", true), - ANDROID_NDK_ARM32("androidNativeArm32", false), - ANDROID_NDK_ARM64("androidNativeArm64", false), - IOS_X64("iosX64", true), - IOS_ARM32("iosArm32", true), - IOS_ARM64("iosArm64", true), - IOS_SIMULATOR_ARM64("iosSimulatorArm64", true), - WATCHOS_X86("watchosX86", true), - WATCHOS_X64("watchosX64", true), - WATCHOS_ARM32("watchosArm32", true), - WATCHOS_ARM64("watchosArm64", true), - WATCHOS_SIMULATOR_ARM64("watchosSimulatorArm64", true), - TVOS_X64("tvosX64", true), - TVOS_ARM64("tvosArm64", true), - TVOS_SIMULATOR_ARM64("tvosSimulatorArm64", true), - MACOS_X64("macosX64", true), - MACOS_ARM64("macosArm64", true), - LINUX_ARM32_HFP("linuxArm32Hfp", false), - LINUX_MIPS32("linuxMips32", false), - LINUX_MIPSEL32("linuxMipsel32", false), - LINUX_X64("linuxX64", true), - LINUX_ARM64("linuxArm64", false), - MINGW_X64("mingwX64", true), - MINGW_X32("mingwX86", false), -} - -val KonanTarget.buildHost: Family - get() = - when (this) { - KonanTarget.ANDROID_X64, - KonanTarget.ANDROID_X86, - KonanTarget.ANDROID_ARM32, - KonanTarget.ANDROID_ARM64, - KonanTarget.LINUX_ARM64, - KonanTarget.LINUX_ARM32_HFP, - KonanTarget.LINUX_MIPS32, - KonanTarget.LINUX_MIPSEL32, - KonanTarget.LINUX_X64 -> Family.LINUX - KonanTarget.MINGW_X86, KonanTarget.MINGW_X64 -> Family.MINGW - KonanTarget.IOS_ARM32, - KonanTarget.IOS_ARM64, - KonanTarget.IOS_X64, - KonanTarget.IOS_SIMULATOR_ARM64, - KonanTarget.WATCHOS_ARM32, - KonanTarget.WATCHOS_ARM64, - KonanTarget.WATCHOS_X86, - KonanTarget.WATCHOS_X64, - KonanTarget.WATCHOS_SIMULATOR_ARM64, - KonanTarget.TVOS_ARM64, - KonanTarget.TVOS_X64, - KonanTarget.TVOS_SIMULATOR_ARM64, - KonanTarget.MACOS_X64, - KonanTarget.MACOS_ARM64 -> Family.OSX - KonanTarget.WASM32 -> throw IllegalStateException("Target $this not supported") - is KonanTarget.ZEPHYR -> throw IllegalStateException("Target $this not supported") - } +package util + +import org.jetbrains.kotlin.konan.target.Family +import org.jetbrains.kotlin.konan.target.KonanTarget + +enum class KotlinTargetDetails( + val presetName: String, + val hasCoroutines: Boolean, +) { + JVM("jvm", true), + ANDROID("android", true), + JS("jsIr", true), + ANDROID_NDK_ARM32("androidNativeArm32", false), + ANDROID_NDK_ARM64("androidNativeArm64", false), + IOS_X64("iosX64", true), + IOS_ARM32("iosArm32", true), + IOS_ARM64("iosArm64", true), + IOS_SIMULATOR_ARM64("iosSimulatorArm64", true), + WATCHOS_X86("watchosX86", true), + WATCHOS_X64("watchosX64", true), + WATCHOS_ARM32("watchosArm32", true), + WATCHOS_ARM64("watchosArm64", true), + WATCHOS_SIMULATOR_ARM64("watchosSimulatorArm64", true), + TVOS_X64("tvosX64", true), + TVOS_ARM64("tvosArm64", true), + TVOS_SIMULATOR_ARM64("tvosSimulatorArm64", true), + MACOS_X64("macosX64", true), + MACOS_ARM64("macosArm64", true), + LINUX_ARM32_HFP("linuxArm32Hfp", false), + LINUX_MIPS32("linuxMips32", false), + LINUX_MIPSEL32("linuxMipsel32", false), + LINUX_X64("linuxX64", true), + LINUX_ARM64("linuxArm64", false), + MINGW_X64("mingwX64", true), + MINGW_X32("mingwX86", false), +} + +val KonanTarget.buildHost: Family + get() = + when (this) { + KonanTarget.ANDROID_X64, + KonanTarget.ANDROID_X86, + KonanTarget.ANDROID_ARM32, + KonanTarget.ANDROID_ARM64, + KonanTarget.LINUX_ARM64, + KonanTarget.LINUX_ARM32_HFP, + KonanTarget.LINUX_MIPS32, + KonanTarget.LINUX_MIPSEL32, + KonanTarget.LINUX_X64 -> Family.LINUX + KonanTarget.MINGW_X86, KonanTarget.MINGW_X64 -> Family.MINGW + KonanTarget.IOS_ARM32, + KonanTarget.IOS_ARM64, + KonanTarget.IOS_X64, + KonanTarget.IOS_SIMULATOR_ARM64, + KonanTarget.WATCHOS_ARM32, + KonanTarget.WATCHOS_ARM64, + KonanTarget.WATCHOS_X86, + KonanTarget.WATCHOS_X64, + KonanTarget.WATCHOS_SIMULATOR_ARM64, + KonanTarget.TVOS_ARM64, + KonanTarget.TVOS_X64, + KonanTarget.TVOS_SIMULATOR_ARM64, + KonanTarget.MACOS_X64, + KonanTarget.MACOS_ARM64 -> Family.OSX + KonanTarget.WASM32 -> throw IllegalStateException("Target $this not supported") + is KonanTarget.ZEPHYR -> throw IllegalStateException("Target $this not supported") + } diff --git a/buildSrc/src/main/kotlin/util/gradle.kt b/buildSrc/src/main/kotlin/util/gradle.kt index febd8858..777340a1 100644 --- a/buildSrc/src/main/kotlin/util/gradle.kt +++ b/buildSrc/src/main/kotlin/util/gradle.kt @@ -1,42 +1,42 @@ -package util - -import groovy.lang.Closure -import java.nio.charset.Charset -import org.gradle.api.Project -import org.gradle.api.provider.Property -import org.jetbrains.kotlin.konan.target.HostManager - -typealias Lambda = R.() -> V - -val CI by lazy { "true".equals(System.getenv("CI"), true) } -val SANDBOX by lazy { "true".equals(System.getenv("SANDBOX"), true) } - -fun Lambda.toClosure(owner: Any? = null, thisObj: Any? = null) = - object : Closure(owner, thisObj) { - @Suppress("UNCHECKED_CAST") - fun doCall() { - with(delegate as R) { this@toClosure() } - } - } - -fun closureOf(owner: Any? = null, thisObj: Any? = null, func: R.() -> V) = - func.toClosure(owner, thisObj) - -infix fun Property.by(value: T) { - set(value) -} - -object Git { - val headCommitHash by lazy { execAndCapture("git rev-parse --verify HEAD") } -} - -fun execAndCapture(cmd: String): String? { - val child = Runtime.getRuntime().exec(cmd) - child.waitFor() - return if (child.exitValue() == 0) { - child.inputStream.readAllBytes().toString(Charset.defaultCharset()).trim() - } else null -} - -val Project.isMainHost: Boolean - get() = HostManager.simpleOsName().equals("${properties["project.mainOS"]}", true) +package util + +import groovy.lang.Closure +import java.nio.charset.Charset +import org.gradle.api.Project +import org.gradle.api.provider.Property +import org.jetbrains.kotlin.konan.target.HostManager + +typealias Lambda = R.() -> V + +val CI by lazy { "true".equals(System.getenv("CI"), true) } +val SANDBOX by lazy { "true".equals(System.getenv("SANDBOX"), true) } + +fun Lambda.toClosure(owner: Any? = null, thisObj: Any? = null) = + object : Closure(owner, thisObj) { + @Suppress("UNCHECKED_CAST") + fun doCall() { + with(delegate as R) { this@toClosure() } + } + } + +fun closureOf(owner: Any? = null, thisObj: Any? = null, func: R.() -> V) = + func.toClosure(owner, thisObj) + +infix fun Property.by(value: T) { + set(value) +} + +object Git { + val headCommitHash by lazy { execAndCapture("git rev-parse --verify HEAD") } +} + +fun execAndCapture(cmd: String): String? { + val child = Runtime.getRuntime().exec(cmd) + child.waitFor() + return if (child.exitValue() == 0) { + child.inputStream.readAllBytes().toString(Charset.defaultCharset()).trim() + } else null +} + +val Project.isMainHost: Boolean + get() = HostManager.simpleOsName().equals("${properties["project.mainOS"]}", true) diff --git a/buildSrc/src/main/kotlin/util/nativeTargetGroup.kt b/buildSrc/src/main/kotlin/util/nativeTargetGroup.kt index a9a7b846..7aba6559 100644 --- a/buildSrc/src/main/kotlin/util/nativeTargetGroup.kt +++ b/buildSrc/src/main/kotlin/util/nativeTargetGroup.kt @@ -1,28 +1,28 @@ -package util - -import org.gradle.kotlin.dsl.get -import org.gradle.kotlin.dsl.invoke -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget - -fun KotlinMultiplatformExtension.nativeTargetGroup( - name: String, - vararg targets: KotlinNativeTarget -): Array { - sourceSets { - val (main, test) = - if (targets.size > 1) { - val commonMain = getByName("commonMain") - val commonTest = getByName("commonTest") - val main = create("${name}Main") { dependsOn(commonMain) } - val test = create("${name}Test") { dependsOn(commonTest) } - main to test - } else (null to null) - - targets.forEach { target -> - main?.let { target.compilations["main"].defaultSourceSet { dependsOn(main) } } - test?.let { target.compilations["test"].defaultSourceSet { dependsOn(test) } } - } - } - return targets -} +package util + +import org.gradle.kotlin.dsl.get +import org.gradle.kotlin.dsl.invoke +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget + +fun KotlinMultiplatformExtension.nativeTargetGroup( + name: String, + vararg targets: KotlinNativeTarget +): Array { + sourceSets { + val (main, test) = + if (targets.size > 1) { + val commonMain = getByName("commonMain") + val commonTest = getByName("commonTest") + val main = create("${name}Main") { dependsOn(commonMain) } + val test = create("${name}Test") { dependsOn(commonTest) } + main to test + } else (null to null) + + targets.forEach { target -> + main?.let { target.compilations["main"].defaultSourceSet { dependsOn(main) } } + test?.let { target.compilations["test"].defaultSourceSet { dependsOn(test) } } + } + } + return targets +} diff --git a/gradle.properties b/gradle.properties index 502a4b87..82824164 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,28 +1,28 @@ -#======================================== Gradle ======================================== -org.gradle.project.targetCompatibility=1.8 -org.gradle.vfs.watch=true -org.gradle.cache=true -#org.gradle.parallel=true -org.gradle.jvmargs=-XX:MaxMetaspaceSize=2g -Xmx2g -#======================================== Kotlin ======================================== -kotlin.style=official -kotlin.stdlib.default.dependency=true -kotlin.js.generate.externals=false -kotlin.js.compiler=ir -kotlin.incremental.js=true -kotlin.mpp.stability.nowarn=true -kotlin.mpp.enableGranularSourceSetsMetadata=true -#kotlin.native.enableDependencyPropagation=false -kotlin.native.ignoreDisabledTargets=true -kapt.includeCompileClasspath=false -#======================================== GitHub ======================================== -gh.owner.id=mpetuska -gh.owner.name=Martynas Petuska -gh.owner.email=martynas@petuska.dev -#======================================= Project ======================================== -group=dev.petuska -description=Kotlin Multiplatform test snapshots (klips) manager -version=0.0.0 -#======================================== Build ========================================= -# linux | macos | windows -project.mainOS=linux +#======================================== Gradle ======================================== +org.gradle.project.targetCompatibility=11 +org.gradle.vfs.watch=true +org.gradle.cache=true +#org.gradle.parallel=true +org.gradle.jvmargs=-XX:MaxMetaspaceSize=2g -Xmx2g +#======================================== Kotlin ======================================== +kotlin.style=official +kotlin.stdlib.default.dependency=true +kotlin.js.generate.externals=false +kotlin.js.compiler=ir +kotlin.incremental.js=true +kotlin.mpp.stability.nowarn=true +kotlin.mpp.enableGranularSourceSetsMetadata=true +#kotlin.native.enableDependencyPropagation=false +kotlin.native.ignoreDisabledTargets=true +kapt.includeCompileClasspath=false +#======================================== GitHub ======================================== +gh.owner.id=mpetuska +gh.owner.name=Martynas Petuska +gh.owner.email=martynas@petuska.dev +#======================================= Project ======================================== +group=dev.petuska +description=Kotlin Multiplatform test snapshots (klips) manager +version=0.0.0 +#======================================== Build ========================================= +# linux | macos | windows +project.mainOS=linux diff --git a/library/klip-api/src/commonMain/kotlin/assertKlip.kt b/library/klip-api/src/commonMain/kotlin/assertKlip.kt index d70651ab..31d0658b 100644 --- a/library/klip-api/src/commonMain/kotlin/assertKlip.kt +++ b/library/klip-api/src/commonMain/kotlin/assertKlip.kt @@ -1,37 +1,37 @@ -package dev.petuska.klip.api - -import dev.petuska.klip.core.Klippable -import dev.petuska.klip.core.int.KlipContext -import dev.petuska.klip.core.int.KlipManager -import dev.petuska.klip.core.int.KlipType -import dev.petuska.klip.core.validate -import kotlin.test.assertEquals - -@DslMarker -@Retention(AnnotationRetention.SOURCE) -internal annotation class KlipDsl - -/** - * Asserts that the given object matches its respective klip - * @param actual value to assert - * @param _context [KlipContext] injected by the compiler with details about the klip file - * @return [actual] - */ -@Klippable -@KlipDsl -public fun assertMatchesKlip(actual: T, _context: KlipContext? = null): T = actual.also { - _context.validate() - val actualStr = actual.toString() - val (klip) = KlipManager.klip(_context, mapOf("type" to "${KlipType.TEXT}")) { actualStr } - assertEquals(klip, actualStr, message = "Value does not match its klip") -} - -/** - * Asserts that the given object matches its respective klip - * @receiver actual value to assert - * @param _context [KlipContext] injected by the compiler with details about the klip file - * @return [this] - */ -@Klippable -@KlipDsl -public fun T.assertKlip(_context: KlipContext? = null): T = assertMatchesKlip(this, _context) +package dev.petuska.klip.api + +import dev.petuska.klip.core.Klippable +import dev.petuska.klip.core.int.KlipContext +import dev.petuska.klip.core.int.KlipManager +import dev.petuska.klip.core.int.KlipType +import dev.petuska.klip.core.validate +import kotlin.test.assertEquals + +@DslMarker +@Retention(AnnotationRetention.SOURCE) +internal annotation class KlipDsl + +/** + * Asserts that the given object matches its respective klip + * @param actual value to assert + * @param _context [KlipContext] injected by the compiler with details about the klip file + * @return [actual] + */ +@Klippable +@KlipDsl +public fun assertMatchesKlip(actual: T, _context: KlipContext? = null): T = actual.also { + _context.validate() + val actualStr = actual.toString() + val (klip) = KlipManager.klip(_context, mapOf("type" to "${KlipType.TEXT}")) { actualStr } + assertEquals(klip, actualStr, message = "Value does not match its klip") +} + +/** + * Asserts that the given object matches its respective klip + * @receiver actual value to assert + * @param _context [KlipContext] injected by the compiler with details about the klip file + * @return [this] + */ +@Klippable +@KlipDsl +public fun T.assertKlip(_context: KlipContext? = null): T = assertMatchesKlip(this, _context) diff --git a/library/klip-api/src/main/AndroidManifest.xml b/library/klip-api/src/main/AndroidManifest.xml index 5b831da6..c361c95f 100644 --- a/library/klip-api/src/main/AndroidManifest.xml +++ b/library/klip-api/src/main/AndroidManifest.xml @@ -1 +1 @@ - + diff --git a/library/klip-core/src/androidMain/kotlin/File.kt b/library/klip-core/src/androidMain/kotlin/File.kt index 82b3cc6e..6e26bf3b 100644 --- a/library/klip-core/src/androidMain/kotlin/File.kt +++ b/library/klip-core/src/androidMain/kotlin/File.kt @@ -1,21 +1,21 @@ -package dev.petuska.klip.core.ext - -import kotlin.io.deleteRecursively as kDeleteRecursively -import kotlin.io.readText as kReadText -import kotlin.io.writeText as kWriteText - -actual typealias File = java.io.File - -actual fun File.writeText(text: String): Unit = kWriteText(text) -actual fun File.readText(): String = kReadText() -actual fun File.deleteRecursively(): Boolean = kDeleteRecursively() - -/** - * Native file separator for the platform (thanks a bunch, windows...) - */ -actual val File.separator: String get() = File.separator - -/** - * Native newline separator for the platform (thanks a bunch, windows...) - */ -actual val File.newline: String get() = System.lineSeparator() +package dev.petuska.klip.core.ext + +import kotlin.io.deleteRecursively as kDeleteRecursively +import kotlin.io.readText as kReadText +import kotlin.io.writeText as kWriteText + +actual typealias File = java.io.File + +actual fun File.writeText(text: String): Unit = kWriteText(text) +actual fun File.readText(): String = kReadText() +actual fun File.deleteRecursively(): Boolean = kDeleteRecursively() + +/** + * Native file separator for the platform (thanks a bunch, windows...) + */ +actual val File.separator: String get() = File.separator + +/** + * Native newline separator for the platform (thanks a bunch, windows...) + */ +actual val File.newline: String get() = System.lineSeparator() diff --git a/library/klip-core/src/commonMain/kotlin/Klippable.kt b/library/klip-core/src/commonMain/kotlin/Klippable.kt index 3929a4a6..37bbfd40 100644 --- a/library/klip-core/src/commonMain/kotlin/Klippable.kt +++ b/library/klip-core/src/commonMain/kotlin/Klippable.kt @@ -1,28 +1,28 @@ -package dev.petuska.klip.core - -import dev.petuska.klip.core.int.KlipContext -import kotlin.contracts.contract - -/** - * Annotation to mark functions to be picked up by the compiler plugin. - * Function signature must contain optional nullable [KlipContext] argument as shown bellow: - * - * ``` - * @Klippable - * fun klippable(..., _context: KlipContext? = null) - * ``` - * @param _context [KlipContext] injected by the compiler with details about the klip file - * @see [KlipContext.validate] - */ -public annotation class Klippable - -/** - * Utility function to verify compiler-injected arguments - * @throws IllegalArgumentException if the validation fails - */ -public fun KlipContext?.validate() { - contract { - returns() implies (this@validate != null) - } - requireNotNull(this) { "KlipContext should not be null and set by the compiler plugin. Did compiler plugin run?" } -} +package dev.petuska.klip.core + +import dev.petuska.klip.core.int.KlipContext +import kotlin.contracts.contract + +/** + * Annotation to mark functions to be picked up by the compiler plugin. + * Function signature must contain optional nullable [KlipContext] argument as shown bellow: + * + * ``` + * @Klippable + * fun klippable(..., _context: KlipContext? = null) + * ``` + * @param _context [KlipContext] injected by the compiler with details about the klip file + * @see [KlipContext.validate] + */ +public annotation class Klippable + +/** + * Utility function to verify compiler-injected arguments + * @throws IllegalArgumentException if the validation fails + */ +public fun KlipContext?.validate() { + contract { + returns() implies (this@validate != null) + } + requireNotNull(this) { "KlipContext should not be null and set by the compiler plugin. Did compiler plugin run?" } +} diff --git a/library/klip-core/src/commonMain/kotlin/ext/File.kt b/library/klip-core/src/commonMain/kotlin/ext/File.kt index ef0a3bb9..b607ad91 100644 --- a/library/klip-core/src/commonMain/kotlin/ext/File.kt +++ b/library/klip-core/src/commonMain/kotlin/ext/File.kt @@ -1,63 +1,63 @@ -package dev.petuska.klip.core.ext - -/** - * Multiplatform wrapper over java.io.File API - */ -public expect class File(path: String) { - /** - * Retrieves parent file - */ - public fun getParentFile(): File? - - /** - * Returns local path to this file - */ - public fun getPath(): String - - /** - * Returns absolute path to this file - */ - public fun getAbsolutePath(): String - - /** - * Recursively makes all directories up to this directory file - */ - public fun mkdirs(): Boolean - - /** - * Checks if the file exists - */ - public fun exists(): Boolean - - /** - * Checks if the file is directory - */ - public fun isDirectory(): Boolean - - override fun toString(): String -} - -/** - * Native file separator for the platform (thanks a bunch, windows...) - */ -public expect val File.separator: String - -/** - * Native newline separator for the platform (thanks a bunch, windows...) - */ -public expect val File.newline: String - -/** - * Writes text to file creating it if needed and fully overwriting any previous content - */ -public expect fun File.writeText(text: String) - -/** - * Reads this file as text - */ -public expect fun File.readText(): String - -/** - * Deletes this file and any subdirectories recursively - */ -public expect fun File.deleteRecursively(): Boolean +package dev.petuska.klip.core.ext + +/** + * Multiplatform wrapper over java.io.File API + */ +public expect class File(path: String) { + /** + * Retrieves parent file + */ + public fun getParentFile(): File? + + /** + * Returns local path to this file + */ + public fun getPath(): String + + /** + * Returns absolute path to this file + */ + public fun getAbsolutePath(): String + + /** + * Recursively makes all directories up to this directory file + */ + public fun mkdirs(): Boolean + + /** + * Checks if the file exists + */ + public fun exists(): Boolean + + /** + * Checks if the file is directory + */ + public fun isDirectory(): Boolean + + override fun toString(): String +} + +/** + * Native file separator for the platform (thanks a bunch, windows...) + */ +public expect val File.separator: String + +/** + * Native newline separator for the platform (thanks a bunch, windows...) + */ +public expect val File.newline: String + +/** + * Writes text to file creating it if needed and fully overwriting any previous content + */ +public expect fun File.writeText(text: String) + +/** + * Reads this file as text + */ +public expect fun File.readText(): String + +/** + * Deletes this file and any subdirectories recursively + */ +public expect fun File.deleteRecursively(): Boolean diff --git a/library/klip-core/src/commonMain/kotlin/ext/cleanupPath.kt b/library/klip-core/src/commonMain/kotlin/ext/cleanupPath.kt index f86b7e7f..d5c32038 100644 --- a/library/klip-core/src/commonMain/kotlin/ext/cleanupPath.kt +++ b/library/klip-core/src/commonMain/kotlin/ext/cleanupPath.kt @@ -1,3 +1,3 @@ -package dev.petuska.klip.core.ext - -internal fun File.cleanupPath(path: String): String = path.replace("/", separator).replace("\\", separator) +package dev.petuska.klip.core.ext + +internal fun File.cleanupPath(path: String): String = path.replace("/", separator).replace("\\", separator) diff --git a/library/klip-core/src/commonMain/kotlin/int/KlipContext.kt b/library/klip-core/src/commonMain/kotlin/int/KlipContext.kt index dc1ae2ed..9e94670c 100644 --- a/library/klip-core/src/commonMain/kotlin/int/KlipContext.kt +++ b/library/klip-core/src/commonMain/kotlin/int/KlipContext.kt @@ -1,14 +1,14 @@ -package dev.petuska.klip.core.int - -/** - * A container to pass in compiler-plugin data - * - * @param path file path to retrieve and save klip for the given context - * @param key key to write the klip under in the file at [path] - * @param update whether the klip should be overridden instead of asserted - */ -public data class KlipContext( - val path: String, - val key: String, - val update: Boolean, -) +package dev.petuska.klip.core.int + +/** + * A container to pass in compiler-plugin data + * + * @param path file path to retrieve and save klip for the given context + * @param key key to write the klip under in the file at [path] + * @param update whether the klip should be overridden instead of asserted + */ +public data class KlipContext( + val path: String, + val key: String, + val update: Boolean, +) diff --git a/library/klip-core/src/commonMain/kotlin/int/KlipManager.kt b/library/klip-core/src/commonMain/kotlin/int/KlipManager.kt index 0f1c9c59..b9db84ad 100644 --- a/library/klip-core/src/commonMain/kotlin/int/KlipManager.kt +++ b/library/klip-core/src/commonMain/kotlin/int/KlipManager.kt @@ -1,146 +1,146 @@ -package dev.petuska.klip.core.int - -import dev.petuska.klip.core.ext.File -import dev.petuska.klip.core.ext.readText -import dev.petuska.klip.core.ext.writeText -import kotlin.native.concurrent.ThreadLocal - -public typealias Klips = MutableMap - -public data class Klip( - val value: String, - val attributes: Map, -) - -/** - * A helper class to assist in reading and writing persisted klips - */ -@ThreadLocal -public object KlipManager { - private const val SEPARATOR = ":::::>>" - private const val SEPARATOR_ATTR = "=" - private const val SEPARATOR_ATTRS = ";" - private const val SEPARATOR_KEY = "$SEPARATOR\n" - private const val SEPARATOR_KLIPS = "\n$SEPARATOR" - private const val SEPARATOR_SOF = "$SEPARATOR KLIPS $SEPARATOR" - private const val SEPARATOR_EOF = "\n$SEPARATOR$SEPARATOR$SEPARATOR\n" - private val klipMatrix = mutableMapOf() - - private fun loadKlips(path: String): Klips = klipMatrix[path] ?: run { - val klips = read(path) - ?.replace("\r\n", "\n") - ?.removePrefix(SEPARATOR_SOF) - ?.removeSuffix(SEPARATOR_EOF) - ?.split(SEPARATOR_KLIPS) - ?.filter(String::isNotEmpty) - ?.associate { kl -> - val split = kl.split(SEPARATOR_KEY) - require(split.size == 2 && !split[0].startsWith("\n")) { - "Corrupted klip at $path:${split.getOrNull(0)?.substringBefore(SEPARATOR)}" - } - val (k, v) = split - val (attr, key) = k.split(SEPARATOR).let { if (it.size > 1) it[0] to it[1] else "" to it[0] } - val attributes = attr.takeIf(String::isNotBlank) - ?.split(SEPARATOR_ATTRS) - ?.associate { it.split(SEPARATOR_ATTR).let { (k, v) -> k to v } } - ?: mapOf() - key to Klip(v, attributes) - }?.toMutableMap() ?: mutableMapOf() - klips.also { - klipMatrix[path] = it - } - } - - private fun saveKlips(path: String, klips: Klips) { - klipMatrix[path]?.putAll(klips) ?: run { klipMatrix[path] = klips } - write(path) { - (klipMatrix[path] ?: mutableMapOf()).entries.joinToString( - separator = "", - prefix = SEPARATOR_SOF, - postfix = SEPARATOR_EOF - ) { (k, v) -> - stringifyKlip(k, v.attributes) { v.value } - } - } - } - - private fun write(path: String, source: () -> String): String { - return File(path).run { - getParentFile()?.mkdirs() - source().also { writeText(it) } - } - } - - private fun read(path: String): String? { - val file = File(path) - return if (file.exists()) { - file.readText() - } else { - null - } - } - - /** - * Converts a [key] and a klip provided by [source] to a writable form that's safe to append to klip file. - * @param key a key to identify the klip within the file - * @param attributes klip attributes - * @return klip-writable form of the value provided by [source] that's safe to append to klip file - */ - public fun stringifyKlip(key: String, attributes: Map, source: () -> String): String { - return SEPARATOR_KLIPS + - attributes.entries.joinToString { (k, v) -> "$k=$v" } + - SEPARATOR + - key + - SEPARATOR_KEY + - source() - } - - /** - * Writes and returns a klip provided by [source] to a klip file specified by [path] under given [key]. - * - * @param path file path to retrieve and save klip for the given context - * @param key key to write the klip under in the file at [path] - * @param attributes klip attributes - * @param source a provider for a string representation to persist - * @return value provided by [source] - */ - public fun writeKlip(path: String, key: String, attributes: Map, source: () -> String): Klip { - val klips = loadKlips(path) - return Klip(source(), attributes).also { saveKlips(path, (klips + mutableMapOf(key to it)).toMutableMap()) } - } - - /** - * Reads a klip from a klip file specified by [path] under given [key]. - * If such klip is not found, a value provided by [source] is persisted and returned instead. - * - * @param path file path to retrieve and save klip for the given context - * @param key key to read or write the klip under in the file at [path] - * @param attributes klip attributes - * @param source a provider for a string representation to persist - * @return read klip if it exists or persisted value that was provided by [source] otherwise - */ - public fun readKlip(path: String, key: String, attributes: Map, source: () -> String): Klip { - val klips = loadKlips(path) - val klip = klips[key] - return when { - klip != null -> klip - else -> writeKlip(path, key, attributes, source) - } - } - - /** - * Writes or returns klip depending on the working mode. - * @param context [KlipContext] containing metadata about the klip - * @param source a provider for a string representation to persist - * @param attributes klip attributes - * @return read klip if it exists and [KlipContext.update]` == false` - * or persisted value that was provided by [source] otherwise - */ - public fun klip(context: KlipContext, attributes: Map, source: () -> String): Klip = with(context) { - return if (update) { - writeKlip(path, key, attributes, source) - } else { - readKlip(path, key, attributes, source) - } - } -} +package dev.petuska.klip.core.int + +import dev.petuska.klip.core.ext.File +import dev.petuska.klip.core.ext.readText +import dev.petuska.klip.core.ext.writeText +import kotlin.native.concurrent.ThreadLocal + +public typealias Klips = MutableMap + +public data class Klip( + val value: String, + val attributes: Map, +) + +/** + * A helper class to assist in reading and writing persisted klips + */ +@ThreadLocal +public object KlipManager { + private const val SEPARATOR = ":::::>>" + private const val SEPARATOR_ATTR = "=" + private const val SEPARATOR_ATTRS = ";" + private const val SEPARATOR_KEY = "$SEPARATOR\n" + private const val SEPARATOR_KLIPS = "\n$SEPARATOR" + private const val SEPARATOR_SOF = "$SEPARATOR KLIPS $SEPARATOR" + private const val SEPARATOR_EOF = "\n$SEPARATOR$SEPARATOR$SEPARATOR\n" + private val klipMatrix = mutableMapOf() + + private fun loadKlips(path: String): Klips = klipMatrix[path] ?: run { + val klips = read(path) + ?.replace("\r\n", "\n") + ?.removePrefix(SEPARATOR_SOF) + ?.removeSuffix(SEPARATOR_EOF) + ?.split(SEPARATOR_KLIPS) + ?.filter(String::isNotEmpty) + ?.associate { kl -> + val split = kl.split(SEPARATOR_KEY) + require(split.size == 2 && !split[0].startsWith("\n")) { + "Corrupted klip at $path:${split.getOrNull(0)?.substringBefore(SEPARATOR)}" + } + val (k, v) = split + val (attr, key) = k.split(SEPARATOR).let { if (it.size > 1) it[0] to it[1] else "" to it[0] } + val attributes = attr.takeIf(String::isNotBlank) + ?.split(SEPARATOR_ATTRS) + ?.associate { it.split(SEPARATOR_ATTR).let { (k, v) -> k to v } } + ?: mapOf() + key to Klip(v, attributes) + }?.toMutableMap() ?: mutableMapOf() + klips.also { + klipMatrix[path] = it + } + } + + private fun saveKlips(path: String, klips: Klips) { + klipMatrix[path]?.putAll(klips) ?: run { klipMatrix[path] = klips } + write(path) { + (klipMatrix[path] ?: mutableMapOf()).entries.joinToString( + separator = "", + prefix = SEPARATOR_SOF, + postfix = SEPARATOR_EOF + ) { (k, v) -> + stringifyKlip(k, v.attributes) { v.value } + } + } + } + + private fun write(path: String, source: () -> String): String { + return File(path).run { + getParentFile()?.mkdirs() + source().also { writeText(it) } + } + } + + private fun read(path: String): String? { + val file = File(path) + return if (file.exists()) { + file.readText() + } else { + null + } + } + + /** + * Converts a [key] and a klip provided by [source] to a writable form that's safe to append to klip file. + * @param key a key to identify the klip within the file + * @param attributes klip attributes + * @return klip-writable form of the value provided by [source] that's safe to append to klip file + */ + public fun stringifyKlip(key: String, attributes: Map, source: () -> String): String { + return SEPARATOR_KLIPS + + attributes.entries.joinToString { (k, v) -> "$k=$v" } + + SEPARATOR + + key + + SEPARATOR_KEY + + source() + } + + /** + * Writes and returns a klip provided by [source] to a klip file specified by [path] under given [key]. + * + * @param path file path to retrieve and save klip for the given context + * @param key key to write the klip under in the file at [path] + * @param attributes klip attributes + * @param source a provider for a string representation to persist + * @return value provided by [source] + */ + public fun writeKlip(path: String, key: String, attributes: Map, source: () -> String): Klip { + val klips = loadKlips(path) + return Klip(source(), attributes).also { saveKlips(path, (klips + mutableMapOf(key to it)).toMutableMap()) } + } + + /** + * Reads a klip from a klip file specified by [path] under given [key]. + * If such klip is not found, a value provided by [source] is persisted and returned instead. + * + * @param path file path to retrieve and save klip for the given context + * @param key key to read or write the klip under in the file at [path] + * @param attributes klip attributes + * @param source a provider for a string representation to persist + * @return read klip if it exists or persisted value that was provided by [source] otherwise + */ + public fun readKlip(path: String, key: String, attributes: Map, source: () -> String): Klip { + val klips = loadKlips(path) + val klip = klips[key] + return when { + klip != null -> klip + else -> writeKlip(path, key, attributes, source) + } + } + + /** + * Writes or returns klip depending on the working mode. + * @param context [KlipContext] containing metadata about the klip + * @param source a provider for a string representation to persist + * @param attributes klip attributes + * @return read klip if it exists and [KlipContext.update]` == false` + * or persisted value that was provided by [source] otherwise + */ + public fun klip(context: KlipContext, attributes: Map, source: () -> String): Klip = with(context) { + return if (update) { + writeKlip(path, key, attributes, source) + } else { + readKlip(path, key, attributes, source) + } + } +} diff --git a/library/klip-core/src/commonMain/kotlin/int/KlipType.kt b/library/klip-core/src/commonMain/kotlin/int/KlipType.kt index eff02065..3cfee149 100644 --- a/library/klip-core/src/commonMain/kotlin/int/KlipType.kt +++ b/library/klip-core/src/commonMain/kotlin/int/KlipType.kt @@ -1,5 +1,5 @@ -package dev.petuska.klip.core.int - -public enum class KlipType { - TEXT, JSON -} +package dev.petuska.klip.core.int + +public enum class KlipType { + TEXT, JSON +} diff --git a/library/klip-core/src/commonTest/kotlin/ext/FileTest.kt b/library/klip-core/src/commonTest/kotlin/ext/FileTest.kt index cd76b9ba..22073799 100644 --- a/library/klip-core/src/commonTest/kotlin/ext/FileTest.kt +++ b/library/klip-core/src/commonTest/kotlin/ext/FileTest.kt @@ -1,60 +1,60 @@ -package dev.petuska.klip.core.ext - -import kotlin.test.BeforeTest -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue - -class FileTest { - private val testDir = File("build/test-dir/FileTest") - - @BeforeTest - fun cleanUp() { - testDir.deleteRecursively() - } - - @Test - fun deleteRecursively() { - val file = File(testDir.getPath()) - assertFalse(file.exists()) - file.mkdirs() - assertTrue(file.exists()) - require(file.deleteRecursively()) { "Error during recursive deletion" } - assertFalse(file.exists()) - } - - @Test - fun getParentFile() { - val file = File("$testDir/ok.kt") - assertEquals(testDir.getPath(), file.getParentFile()?.getPath()) - } - - @Test - fun getPath() { - val file = File("$testDir/ok.kt") - assertEquals("$testDir${testDir.separator}ok.kt", file.getPath()) - } - - @Test - fun getAbsolutePath() { - val file = File("$testDir/ok.kt") - file.mkdirs() - assertEquals("${file.getParentFile()?.getAbsolutePath()}${testDir.separator}ok.kt", file.getAbsolutePath()) - } - - @Test - fun mkdirs() { - val file = File("$testDir/subdir") - assertTrue(file.mkdirs()) - assertTrue(file.exists()) - } - - @Test - fun exists() { - val file = File(testDir.getPath()) - assertFalse(file.exists()) - file.mkdirs() - assertTrue(file.exists()) - } -} +package dev.petuska.klip.core.ext + +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class FileTest { + private val testDir = File("build/test-dir/FileTest") + + @BeforeTest + fun cleanUp() { + testDir.deleteRecursively() + } + + @Test + fun deleteRecursively() { + val file = File(testDir.getPath()) + assertFalse(file.exists()) + file.mkdirs() + assertTrue(file.exists()) + require(file.deleteRecursively()) { "Error during recursive deletion" } + assertFalse(file.exists()) + } + + @Test + fun getParentFile() { + val file = File("$testDir/ok.kt") + assertEquals(testDir.getPath(), file.getParentFile()?.getPath()) + } + + @Test + fun getPath() { + val file = File("$testDir/ok.kt") + assertEquals("$testDir${testDir.separator}ok.kt", file.getPath()) + } + + @Test + fun getAbsolutePath() { + val file = File("$testDir/ok.kt") + file.mkdirs() + assertEquals("${file.getParentFile()?.getAbsolutePath()}${testDir.separator}ok.kt", file.getAbsolutePath()) + } + + @Test + fun mkdirs() { + val file = File("$testDir/subdir") + assertTrue(file.mkdirs()) + assertTrue(file.exists()) + } + + @Test + fun exists() { + val file = File(testDir.getPath()) + assertFalse(file.exists()) + file.mkdirs() + assertTrue(file.exists()) + } +} diff --git a/library/klip-core/src/fallbackMain/kotlin/File.kt b/library/klip-core/src/fallbackMain/kotlin/File.kt index 388146c3..6697430a 100644 --- a/library/klip-core/src/fallbackMain/kotlin/File.kt +++ b/library/klip-core/src/fallbackMain/kotlin/File.kt @@ -1,67 +1,67 @@ -package dev.petuska.klip.core.ext - -/** - * Multiplatform wrapper over java.io.File - */ -public actual class File actual constructor(private val path: String) { - init { - require(path.isNotEmpty()) { "Path cannot be empty" } - } - - /** - * Retrieves parent file - */ - public actual fun getParentFile(): File? = TODO() - - /** - * Returns local path to this file - */ - public actual fun getPath(): String = TODO() - - /** - * Returns absolute path to this file - */ - public actual fun getAbsolutePath(): String = TODO() - - /** - * Recursively makes all directories up to this directory file - */ - public actual fun mkdirs(): Boolean = TODO() - - /** - * Checks if the file exsists - */ - public actual fun exists(): Boolean = TODO() - - /** - * checks if the file is directory - */ - public actual fun isDirectory(): Boolean = TODO() - - actual override fun toString(): String = TODO() -} - -/** - * Writes text to file creating it if needed and fully overwriting any previous content - */ -public actual fun File.writeText(text: String): Unit = TODO() - -/** - * Reads this file as text - */ -public actual fun File.readText(): String = TODO() - -/** - * Deletes this file and any subdirectories recursively - */ -public actual fun File.deleteRecursively(): Boolean = TODO() - -/** - * Native file separator for the platform (thanks a bunch, windows...) - */ -public actual val File.separator: String get() = "/" - -/** - * Native newline separator for the platform (thanks a bunch, windows...) - */ -public actual val File.newline: String get() = "\n" +package dev.petuska.klip.core.ext + +/** + * Multiplatform wrapper over java.io.File + */ +public actual class File actual constructor(private val path: String) { + init { + require(path.isNotEmpty()) { "Path cannot be empty" } + } + + /** + * Retrieves parent file + */ + public actual fun getParentFile(): File? = TODO() + + /** + * Returns local path to this file + */ + public actual fun getPath(): String = TODO() + + /** + * Returns absolute path to this file + */ + public actual fun getAbsolutePath(): String = TODO() + + /** + * Recursively makes all directories up to this directory file + */ + public actual fun mkdirs(): Boolean = TODO() + + /** + * Checks if the file exsists + */ + public actual fun exists(): Boolean = TODO() + + /** + * checks if the file is directory + */ + public actual fun isDirectory(): Boolean = TODO() + + actual override fun toString(): String = TODO() +} + +/** + * Writes text to file creating it if needed and fully overwriting any previous content + */ +public actual fun File.writeText(text: String): Unit = TODO() + +/** + * Reads this file as text + */ +public actual fun File.readText(): String = TODO() + +/** + * Deletes this file and any subdirectories recursively + */ +public actual fun File.deleteRecursively(): Boolean = TODO() + +/** + * Native file separator for the platform (thanks a bunch, windows...) + */ +public actual val File.separator: String get() = "/" + +/** + * Native newline separator for the platform (thanks a bunch, windows...) + */ +public actual val File.newline: String get() = "\n" diff --git a/library/klip-core/src/jsMain/kotlin/File.kt b/library/klip-core/src/jsMain/kotlin/File.kt index 67ecea37..fb883ff2 100644 --- a/library/klip-core/src/jsMain/kotlin/File.kt +++ b/library/klip-core/src/jsMain/kotlin/File.kt @@ -1,87 +1,87 @@ -package dev.petuska.klip.core.ext - -internal external fun require(module: String): dynamic - -private val jsPath = require("path") -private val fs = require("fs") - -/** - * Multiplatform wrapper over java.io.File API - */ -public actual class File actual constructor(path: String) { - private val path: String - - init { - this.path = cleanupPath(path) - } - - /** - * Returns absolute path to this file - */ - public actual fun getAbsolutePath(): String = jsPath.normalize(path) - - /** - * Returns local path to this file - */ - public actual fun getPath(): String = path - - /** - * Recursively makes all directories up to this directory file - */ - public actual fun mkdirs(): Boolean = kotlin.runCatching { - fs.mkdirSync(path, jsObject { recursive = true }) - }.isSuccess - - /** - * Retrieves parent file - */ - public actual fun getParentFile(): File? { - val p = jsPath.dirname(path) - return if (p != null && p != undefined) { - File(p) - } else null - } - - /** - * Checks if the file exsists - */ - public actual fun exists(): Boolean = fs.existsSync(path) - - /** - * checks if the file is directory - */ - public actual fun isDirectory(): Boolean = fs.lstatSync(path).isDirectory() - - actual override fun toString(): String = getPath() -} - -/** - * Writes text to file creating it if needed and fully overwriting any previous content - */ -public actual fun File.writeText(text: String): Unit = fs.writeFileSync(getAbsolutePath(), text, "utf8") - -/** - * Reads this file as text - */ -public actual fun File.readText(): String = fs.readFileSync(getAbsolutePath(), "utf8") - -/** - * Deletes this file and any subdirectories recursively - */ -public actual fun File.deleteRecursively(): Boolean = runCatching { - if (isDirectory()) { - fs.rmdirSync(getPath(), jsObject { recursive = true }) - } else { - fs.unlinkSync(getPath()) - } -}.isSuccess - -/** - * Native file separator for the platform (thanks a bunch, windows...) - */ -public actual val File.separator: String get() = jsPath.sep - -/** - * Native newline separator for the platform (thanks a bunch, windows...) - */ -public actual val File.newline: String get() = require("os").EOL +package dev.petuska.klip.core.ext + +internal external fun require(module: String): dynamic + +private val jsPath = require("path") +private val fs = require("fs") + +/** + * Multiplatform wrapper over java.io.File API + */ +public actual class File actual constructor(path: String) { + private val path: String + + init { + this.path = cleanupPath(path) + } + + /** + * Returns absolute path to this file + */ + public actual fun getAbsolutePath(): String = jsPath.normalize(path) + + /** + * Returns local path to this file + */ + public actual fun getPath(): String = path + + /** + * Recursively makes all directories up to this directory file + */ + public actual fun mkdirs(): Boolean = kotlin.runCatching { + fs.mkdirSync(path, jsObject { recursive = true }) + }.isSuccess + + /** + * Retrieves parent file + */ + public actual fun getParentFile(): File? { + val p = jsPath.dirname(path) + return if (p != null && p != undefined) { + File(p) + } else null + } + + /** + * Checks if the file exsists + */ + public actual fun exists(): Boolean = fs.existsSync(path) + + /** + * checks if the file is directory + */ + public actual fun isDirectory(): Boolean = fs.lstatSync(path).isDirectory() + + actual override fun toString(): String = getPath() +} + +/** + * Writes text to file creating it if needed and fully overwriting any previous content + */ +public actual fun File.writeText(text: String): Unit = fs.writeFileSync(getAbsolutePath(), text, "utf8") + +/** + * Reads this file as text + */ +public actual fun File.readText(): String = fs.readFileSync(getAbsolutePath(), "utf8") + +/** + * Deletes this file and any subdirectories recursively + */ +public actual fun File.deleteRecursively(): Boolean = runCatching { + if (isDirectory()) { + fs.rmdirSync(getPath(), jsObject { recursive = true }) + } else { + fs.unlinkSync(getPath()) + } +}.isSuccess + +/** + * Native file separator for the platform (thanks a bunch, windows...) + */ +public actual val File.separator: String get() = jsPath.sep + +/** + * Native newline separator for the platform (thanks a bunch, windows...) + */ +public actual val File.newline: String get() = require("os").EOL diff --git a/library/klip-core/src/jsMain/kotlin/util.kt b/library/klip-core/src/jsMain/kotlin/util.kt index 6ca15f07..fb74f39d 100644 --- a/library/klip-core/src/jsMain/kotlin/util.kt +++ b/library/klip-core/src/jsMain/kotlin/util.kt @@ -1,3 +1,3 @@ -package dev.petuska.klip.core.ext - -internal fun jsObject(builder: T.() -> Unit) = js("{}").unsafeCast().apply(builder) +package dev.petuska.klip.core.ext + +internal fun jsObject(builder: T.() -> Unit) = js("{}").unsafeCast().apply(builder) diff --git a/library/klip-core/src/jvmMain/kotlin/File.kt b/library/klip-core/src/jvmMain/kotlin/File.kt index 29faf1ec..89c34942 100644 --- a/library/klip-core/src/jvmMain/kotlin/File.kt +++ b/library/klip-core/src/jvmMain/kotlin/File.kt @@ -1,21 +1,21 @@ -package dev.petuska.klip.core.ext - -import kotlin.io.deleteRecursively as kDeleteRecursively -import kotlin.io.readText as kReadText -import kotlin.io.writeText as kWriteText - -public actual typealias File = java.io.File - -public actual fun File.writeText(text: String): Unit = kWriteText(text) -public actual fun File.readText(): String = kReadText() -public actual fun File.deleteRecursively(): Boolean = kDeleteRecursively() - -/** - * Native file separator for the platform (thanks a bunch, windows...) - */ -public actual val File.separator: String get() = File.separator - -/** - * Native newline separator for the platform (thanks a bunch, windows...) - */ -public actual val File.newline: String get() = System.lineSeparator() +package dev.petuska.klip.core.ext + +import kotlin.io.deleteRecursively as kDeleteRecursively +import kotlin.io.readText as kReadText +import kotlin.io.writeText as kWriteText + +public actual typealias File = java.io.File + +public actual fun File.writeText(text: String): Unit = kWriteText(text) +public actual fun File.readText(): String = kReadText() +public actual fun File.deleteRecursively(): Boolean = kDeleteRecursively() + +/** + * Native file separator for the platform (thanks a bunch, windows...) + */ +public actual val File.separator: String get() = File.separator + +/** + * Native newline separator for the platform (thanks a bunch, windows...) + */ +public actual val File.newline: String get() = System.lineSeparator() diff --git a/library/klip-core/src/main/AndroidManifest.xml b/library/klip-core/src/main/AndroidManifest.xml index 1665f59f..794bc550 100644 --- a/library/klip-core/src/main/AndroidManifest.xml +++ b/library/klip-core/src/main/AndroidManifest.xml @@ -1 +1 @@ - + diff --git a/library/klip-core/src/mingwMain/kotlin/File.kt b/library/klip-core/src/mingwMain/kotlin/File.kt index 81c220e7..dec4e506 100644 --- a/library/klip-core/src/mingwMain/kotlin/File.kt +++ b/library/klip-core/src/mingwMain/kotlin/File.kt @@ -1,20 +1,20 @@ -package dev.petuska.klip.core.ext - -import platform.posix.mkdir - -/** - * Native file separator for the platform (thanks a bunch, windows...) - */ -public actual val File.separator: String get() = "\\" - -/** - * Native newline separator for the platform (thanks a bunch, windows...) - */ -public actual val File.newline: String get() = "\r\n" - -internal actual fun mppMkdir(path: String, permissions: Int): Int = mkdir(path) - -/** - * Checks if file path is starting from root (thanks a bunch, windows...) - */ -internal actual val File.isRooted: Boolean get() = getPath().matches("^[A-z]:\\$separator".toRegex()) +package dev.petuska.klip.core.ext + +import platform.posix.mkdir + +/** + * Native file separator for the platform (thanks a bunch, windows...) + */ +public actual val File.separator: String get() = "\\" + +/** + * Native newline separator for the platform (thanks a bunch, windows...) + */ +public actual val File.newline: String get() = "\r\n" + +internal actual fun mppMkdir(path: String, permissions: Int): Int = mkdir(path) + +/** + * Checks if file path is starting from root (thanks a bunch, windows...) + */ +internal actual val File.isRooted: Boolean get() = getPath().matches("^[A-z]:\\$separator".toRegex()) diff --git a/library/klip-core/src/unixMain/kotlin/File.kt b/library/klip-core/src/unixMain/kotlin/File.kt index a34f00ad..b4ba03b0 100644 --- a/library/klip-core/src/unixMain/kotlin/File.kt +++ b/library/klip-core/src/unixMain/kotlin/File.kt @@ -1,21 +1,21 @@ -package dev.petuska.klip.core.ext - -import kotlinx.cinterop.convert -import platform.posix.mkdir - -/** - * Native file separator for the platform (thanks a bunch, windows...) - */ -public actual val File.separator: String get() = "/" - -/** - * Native newline separator for the platform (thanks a bunch, windows...) - */ -public actual val File.newline: String get() = "\n" - -/** - * Checks if file path is starting from root (thanks a bunch, windows...) - */ -internal actual val File.isRooted: Boolean get() = getPath().startsWith("/") - -internal actual fun mppMkdir(path: String, permissions: Int): Int = mkdir(path, permissions.convert()) +package dev.petuska.klip.core.ext + +import kotlinx.cinterop.convert +import platform.posix.mkdir + +/** + * Native file separator for the platform (thanks a bunch, windows...) + */ +public actual val File.separator: String get() = "/" + +/** + * Native newline separator for the platform (thanks a bunch, windows...) + */ +public actual val File.newline: String get() = "\n" + +/** + * Checks if file path is starting from root (thanks a bunch, windows...) + */ +internal actual val File.isRooted: Boolean get() = getPath().startsWith("/") + +internal actual fun mppMkdir(path: String, permissions: Int): Int = mkdir(path, permissions.convert()) diff --git a/plugin/klip-gradle-plugin/src/main/kotlin/KlipPlugin.kt b/plugin/klip-gradle-plugin/src/main/kotlin/KlipPlugin.kt index 01b3043c..51fe8ef1 100644 --- a/plugin/klip-gradle-plugin/src/main/kotlin/KlipPlugin.kt +++ b/plugin/klip-gradle-plugin/src/main/kotlin/KlipPlugin.kt @@ -22,8 +22,8 @@ class KlipPlugin : KotlinCompilerPluginSupportPlugin { val extension = createExtension() tasks.register("klipUpdate", KlipUpdateTask::class.java) tasks.withType(Test::class.java) { - it.inputs.property("klip.update", "${extension.update}") it.inputs.property("klip.enabled", "${extension.enabled}") + it.inputs.property("klip.update", "${extension.update}") it.environment("KLIP_UPDATE", "${extension.update}") } } diff --git a/plugin/klip-gradle-plugin/src/main/kotlin/util/KlipOption.kt b/plugin/klip-gradle-plugin/src/main/kotlin/util/KlipOption.kt index d82038c6..ab0961c2 100644 --- a/plugin/klip-gradle-plugin/src/main/kotlin/util/KlipOption.kt +++ b/plugin/klip-gradle-plugin/src/main/kotlin/util/KlipOption.kt @@ -56,14 +56,14 @@ sealed class KlipOption( name = "scopeFunction", default = listOf( - "io.kotest.core.spec.style.scopes.FunSpecRootContext.test", - "io.kotest.core.spec.style.scopes.DescribeSpecContainerContext.it", - "io.kotest.core.spec.style.scopes.BehaviorSpecWhenContainerContext.Then", - "io.kotest.core.spec.style.scopes.BehaviorSpecWhenContainerContext.then", - "io.kotest.core.spec.style.scopes.WordSpecShouldContainerContext.invoke", - "io.kotest.core.spec.style.scopes.FreeSpecContainerContext.invoke", - "io.kotest.core.spec.style.scopes.FeatureSpecContainerContext.scenario", - "io.kotest.core.spec.style.scopes.ExpectSpecContainerContext.expect", + "io.kotest.core.spec.style.scopes.FunSpecRootScope.test", + "io.kotest.core.spec.style.scopes.DescribeSpecContainerScope.it", + "io.kotest.core.spec.style.scopes.BehaviorSpecWhenContainerScope.Then", + "io.kotest.core.spec.style.scopes.BehaviorSpecWhenContainerScope.then", + "io.kotest.core.spec.style.scopes.WordSpecShouldContainerScope.invoke", + "io.kotest.core.spec.style.scopes.FreeSpecContainerScope.invoke", + "io.kotest.core.spec.style.scopes.FeatureSpecContainerScope.scenario", + "io.kotest.core.spec.style.scopes.ExpectSpecContainerScope.expect", ), ) } diff --git a/plugin/klip-kotlin-plugin/src/main/kotlin/KlipComponentRegistrar.kt b/plugin/klip-kotlin-plugin/src/main/kotlin/KlipComponentRegistrar.kt index f662ecf0..ce789714 100644 --- a/plugin/klip-kotlin-plugin/src/main/kotlin/KlipComponentRegistrar.kt +++ b/plugin/klip-kotlin-plugin/src/main/kotlin/KlipComponentRegistrar.kt @@ -3,6 +3,7 @@ package dev.petuska.klip.plugin import com.google.auto.service.AutoService import dev.petuska.klip.plugin.util.KlipOption import dev.petuska.klip.plugin.util.KlipSettings +import dev.petuska.klip.plugin.util.debug import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.messages.MessageCollector @@ -22,6 +23,7 @@ class KlipComponentRegistrar : ComponentRegistrar { project: MockProject, configuration: CompilerConfiguration ) { + debug { java.io.File("").canonicalPath } val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) IrGenerationExtension.registerExtension( diff --git a/plugin/klip-kotlin-plugin/src/main/kotlin/util/Debugger.kt b/plugin/klip-kotlin-plugin/src/main/kotlin/util/Debugger.kt new file mode 100644 index 00000000..751f3f1d --- /dev/null +++ b/plugin/klip-kotlin-plugin/src/main/kotlin/util/Debugger.kt @@ -0,0 +1,23 @@ +package dev.petuska.klip.plugin.util + +import java.io.File + +private object Debugger { + private val file by lazy { + File("${System.getProperty("user.home")}/IdeaProjects/klip/sandbox/klip.log").also { + it.writeText("PWD: ${File("").canonicalPath}") + } + } + private val enabled = System.getenv("KLIP_DEBUG")?.let { !it.equals("false", true) } == true + fun log(text: () -> String) { + if (enabled) + text().let { + println("DEBUG: $it") + file.appendText("${it}\n") + } + } +} + +fun debug(text: () -> String) { + Debugger.log(text) +} diff --git a/sandbox/build.gradle.kts b/sandbox/build.gradle.kts index eaf19333..13364c9d 100644 --- a/sandbox/build.gradle.kts +++ b/sandbox/build.gradle.kts @@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile plugins { id("dev.petuska.klip") - id("io.kotest.multiplatform") version "5.0.0.5" + id("io.kotest.multiplatform") version "5.0.0" kotlin("multiplatform") id("com.android.library") idea @@ -21,6 +21,10 @@ android { minSdkVersion(1) targetSdkVersion(31) } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } } allprojects { @@ -39,15 +43,22 @@ allprojects { } tasks { afterEvaluate { + withType(AbstractKotlinCompile::class) { + val debug = + (project.findProperty("klip.debug") ?: System.getenv("KLIP_DEBUG"))?.toString()?.let { + !it.equals("false", true) + } == true + if (debug) { + outputs.upToDateWhen { false } + } + } if (tasks.findByName("compile") == null) { register("compile") { dependsOn(withType(AbstractKotlinCompile::class)) group = "build" } } - val testTasks = withType { - useJUnitPlatform() - } + val testTasks = withType { useJUnitPlatform() } if (tasks.findByName("allTests") == null) { register("allTests") { dependsOn(testTasks) @@ -102,6 +113,7 @@ kotlin { named("androidTest") { dependencies { implementation(kotlin("test-junit5")) + implementation("io.kotest:kotest-framework-engine:_") implementation("io.kotest:kotest-runner-junit5:_") } } @@ -114,6 +126,7 @@ kotlin { } named("jsTest") { dependencies { + implementation("io.kotest:kotest-framework-engine:_") implementation(kotlin("test-js")) } } diff --git a/sandbox/settings.gradle.kts b/sandbox/settings.gradle.kts index 99bd8177..43caed7e 100644 --- a/sandbox/settings.gradle.kts +++ b/sandbox/settings.gradle.kts @@ -1,17 +1,18 @@ -pluginManagement { - repositories { - gradlePluginPortal() - google() - } -} - -plugins { - id("de.fayard.refreshVersions") version "0.21.0" - id("com.gradle.enterprise") version "3.6.4" -} - -refreshVersions { extraArtifactVersionKeyRules(file("versions.rules")) } - -rootProject.name = "sandbox" -includeBuild("../") -include(":jvm", "js") +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + google() + } +} + +plugins { + id("de.fayard.refreshVersions") version "0.21.0" + id("com.gradle.enterprise") version "3.6.4" +} + +refreshVersions { extraArtifactVersionKeyRules(file("versions.rules")) } + +rootProject.name = "sandbox" +includeBuild("../") +include(":jvm", "js") diff --git a/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmBehaviourSpec.kt.klip b/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmBehaviourSpec.kt.klip index 4159ce5c..ba9b27f4 100644 --- a/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmBehaviourSpec.kt.klip +++ b/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmBehaviourSpec.kt.klip @@ -5,4 +5,10 @@ kotest zero kotest one :::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.BehaviorSpecWhenContainerContext.Then(test two)#0:::::>> kotest 2 +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.BehaviorSpecWhenContainerScope.Then(test one)#0:::::>> +kotest zero +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.BehaviorSpecWhenContainerScope.Then(test one)#1:::::>> +kotest one +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.BehaviorSpecWhenContainerScope.Then(test two)#0:::::>> +kotest 2 :::::>>:::::>>:::::>> diff --git a/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmDescribeSpec.kt.klip b/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmDescribeSpec.kt.klip index 747e333d..195a686b 100644 --- a/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmDescribeSpec.kt.klip +++ b/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmDescribeSpec.kt.klip @@ -5,4 +5,10 @@ kotest zero kotest one :::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.DescribeSpecContainerContext.it(test two)#0:::::>> kotest 2 +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.DescribeSpecContainerScope.it(test one)#0:::::>> +kotest zero +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.DescribeSpecContainerScope.it(test one)#1:::::>> +kotest one +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.DescribeSpecContainerScope.it(test two)#0:::::>> +kotest 2 :::::>>:::::>>:::::>> diff --git a/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmExpectSpec.kt.klip b/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmExpectSpec.kt.klip index 471ee5bb..50092bdd 100644 --- a/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmExpectSpec.kt.klip +++ b/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmExpectSpec.kt.klip @@ -5,4 +5,10 @@ kotest zero kotest one :::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.ExpectSpecContainerContext.expect(test two)#0:::::>> kotest 2 +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.ExpectSpecContainerScope.expect(test one)#0:::::>> +kotest zero +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.ExpectSpecContainerScope.expect(test one)#1:::::>> +kotest one +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.ExpectSpecContainerScope.expect(test two)#0:::::>> +kotest 2 :::::>>:::::>>:::::>> diff --git a/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmFeatureSpec.kt.klip b/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmFeatureSpec.kt.klip index ed0fc282..7bccc0bc 100644 --- a/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmFeatureSpec.kt.klip +++ b/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmFeatureSpec.kt.klip @@ -5,4 +5,10 @@ kotest zero kotest one :::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.FeatureSpecContainerContext.scenario(test two)#0:::::>> kotest 2 +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.FeatureSpecContainerScope.scenario(test one)#0:::::>> +kotest zero +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.FeatureSpecContainerScope.scenario(test one)#1:::::>> +kotest one +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.FeatureSpecContainerScope.scenario(test two)#0:::::>> +kotest 2 :::::>>:::::>>:::::>> diff --git a/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmFreeSpec.kt.klip b/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmFreeSpec.kt.klip index 4ab7fc3c..b1db8872 100644 --- a/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmFreeSpec.kt.klip +++ b/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmFreeSpec.kt.klip @@ -5,4 +5,10 @@ kotest zero kotest one :::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.FreeSpecContainerContext.invoke(test two)#0:::::>> kotest 2 +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.FreeSpecContainerScope.invoke(test one)#0:::::>> +kotest zero +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.FreeSpecContainerScope.invoke(test one)#1:::::>> +kotest one +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.FreeSpecContainerScope.invoke(test two)#0:::::>> +kotest 2 :::::>>:::::>>:::::>> diff --git a/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmWordSpec.kt.klip b/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmWordSpec.kt.klip index dc856d73..12166752 100644 --- a/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmWordSpec.kt.klip +++ b/sandbox/src/jvmTest/kotlin/kotest/__klips__/JvmWordSpec.kt.klip @@ -5,4 +5,10 @@ kotest zero kotest one :::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.WordSpecShouldContainerContext.invoke(test two)#0:::::>> kotest 2 +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.WordSpecShouldContainerScope.invoke(test one)#0:::::>> +kotest zero +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.WordSpecShouldContainerScope.invoke(test one)#1:::::>> +kotest one +:::::>>type=TEXT:::::>>io.kotest.core.spec.style.scopes.WordSpecShouldContainerScope.invoke(test two)#0:::::>> +kotest 2 :::::>>:::::>>:::::>> diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 95d1ea65..f7a72dfd 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -1 +1 @@ - + diff --git a/versions.properties b/versions.properties index 22db9e65..99e08447 100644 --- a/versions.properties +++ b/versions.properties @@ -11,11 +11,11 @@ plugin.android=4.2.2 plugin.com.gradle.plugin-publish=0.18.0 version.com.diffplug.spotless..spotless-plugin-gradle=6.0.0 version.com.github.jakemarsden..git-hooks-gradle-plugin=0.0.2 -version.org.jetbrains.dokka..dokka-gradle-plugin=1.5.31 +version.org.jetbrains.dokka..dokka-gradle-plugin=1.6.0 version.com.github.gmazzo..gradle-buildconfig-plugin=3.0.3 version.io.github.gradle-nexus..publish-plugin=1.1.0 #====================================== Libraries ======================================= version.com.github.tschuchortdev..kotlin-compile-testing=1.4.6 version.google.auto.service=1.0.1 version.kotlin=1.6.0 -version.kotest=5.0.0.M3 +version.kotest=5.0.0