more KMP targets

This commit is contained in:
Sergey Chernov 2024-02-26 12:44:44 +03:00
parent 3c33eb3bd9
commit e9082af4de
8 changed files with 221 additions and 13 deletions

View File

@ -17,6 +17,7 @@ repositories {
} }
kotlin { kotlin {
jvmToolchain(17)
jvm { jvm {
compilations.all { compilations.all {
kotlinOptions.jvmTarget = "1.8" kotlinOptions.jvmTarget = "1.8"
@ -48,15 +49,32 @@ kotlin {
} }
} }
val hostOs = System.getProperty("os.name") // val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows") // val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when { // val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native") // hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native") // hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native") // isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.") // else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
// }
linuxX64("native") {
binaries.staticLib {
baseName = "mp_bintools"
}
} }
wasmJs {
browser()
binaries.executable()
}
mingwX64() {
binaries.staticLib {
baseName = "mp_bintools"
}
}
sourceSets { sourceSets {
all { all {
@ -66,29 +84,35 @@ kotlin {
} }
val commonMain by getting { val commonMain by getting {
dependencies { dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
// this is actually a bug: we need only the core, but bare core causes strange errors // this is actually a bug: we need only the core, but bare core causes strange errors
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
// api("net.sergeych:mp_stools:[1.3.3,)") // api("net.sergeych:mp_stools:[1.3.3,)")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0")
} }
} }
val commonTest by getting { val commonTest by getting {
dependencies { dependencies {
implementation(kotlin("test")) implementation(kotlin("test"))
implementation("net.sergeych:mp_stools:1.4.1") implementation("net.sergeych:mp_stools:1.4.4-SNAPSHOT")
} }
} }
val jvmMain by getting val jvmMain by getting
val jvmTest by getting val jvmTest by getting
val jsMain by getting { val jsMain by getting {
dependencies { dependencies {
implementation("net.sergeych:mp_stools:1.4.3") implementation("net.sergeych:mp_stools:1.4.4-SNAPSHOT")
} }
} }
val jsTest by getting val jsTest by getting
val nativeMain by getting val nativeMain by getting
val nativeTest by getting val nativeTest by getting
val wasmJsMain by getting {
dependencies {
implementation("net.sergeych:mp_stools:1.4.4-SNAPSHOT")
}
}
val wasmJsTest by getting
} }
publishing { publishing {

View File

@ -1,6 +1,5 @@
package net.sergeych.synctools package net.sergeych.synctools
import net.sergeych.tools.AtomicCounter
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals

View File

@ -0,0 +1,25 @@
package net.sergeych.bintools
/**
* Create per-platform default named storage.
*
* - In the browser, it uses the `Window.localStorage` prefixing items
* by a string containing the [name]
*
* - In the JVM environment it uses folder-based storage on the file system. The name
* is considered to be a folder name (the whole path which will be automatically created)
* using the following rules:
* - when the name starts with slash (`/`) it is treated as an absolute path to a folder
* - when the name contains slash, it is considered to be a relative folder to the
* `User.home` directory, like "`~/`" on unix systems.
* - otherwise, the folder will be created in "`~/.local_storage`" parent directory
* (which also will be created if needed).
*
* - For the native platorms it is not yet implemented (but will be soon).
*
* See [DataKVStorage] and [DataProvider] to implement a KVStorage on filesystems and like,
* and `FileDataProvider` class on JVM target.
*/
actual fun defaultNamedStorage(name: String): KVStorage {
TODO("Not yet implemented")
}

View File

@ -0,0 +1,17 @@
package net.sergeych.synctools
import kotlinx.atomicfu.locks.ReentrantLock
/**
* Native implementation uses `ReentrantLock`]
*/
actual fun ProtectedOp(): ProtectedOpImplementation = object : ProtectedOpImplementation {
private val access = ReentrantLock()
override fun lock() {
access.lock()
}
override fun unlock() {
access.unlock()
}
}

View File

@ -0,0 +1,34 @@
package net.sergeych.synctools
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
actual class WaitHandle {
private val channel = Channel<Unit>()
actual fun await(milliseconds: Long): Boolean {
return runBlocking {
try {
if( milliseconds > 0) {
withTimeout(milliseconds) {
channel.receive()
true
}
}
else {
channel.receive()
true
}
}
catch(_: TimeoutCancellationException) {
false
}
}
}
actual fun wakeUp() {
runBlocking { channel.send(Unit) }
}
}

View File

@ -0,0 +1,63 @@
package net.sergeych.bintools
import kotlinx.browser.localStorage
import net.sergeych.mp_tools.decodeBase64Compact
import net.sergeych.mp_tools.encodeToBase64Compact
import org.w3c.dom.Storage
import org.w3c.dom.set
/**
* Create per-platform default named storage.
*
* - In the browser, it uses the `Window.localStorage` prefixing items
* by a string containing the [name]
*
* - In the JVM environment it uses folder-based storage on the file system. The name
* is considered to be a folder name (the whole path which will be automatically created)
* using the following rules:
* - when the name starts with slash (`/`) it is treated as an absolute path to a folder
* - when the name contains slash, it is considered to be a relative folder to the
* `User.home` directory, like "`~/`" on unix systems.
* - otherwise, the folder will be created in "`~/.local_storage`" parent directory
* (which also will be created if needed).
*
* - For the native platorms it is not yet implemented (but will be soon).
*
* See [DataKVStorage] and [DataProvider] to implement a KVStorage on filesystems and like,
* and `FileDataProvider` class on JVM target.
*/
actual fun defaultNamedStorage(name: String): KVStorage = BrowserKVStorage(name, localStorage)
/**
* Default KV storage in browser. Use if with `localStorage` or `sessionStorage`. It uses
* prefix for storage values not to collide with other data, beware of it.
*/
class BrowserKVStorage(keyPrefix: String, private val bst: Storage) : KVStorage {
private val prefix = "$keyPrefix:"
fun k(key: String) = "$prefix$key"
override fun get(key: String): ByteArray? {
return bst.getItem(k(key))?.decodeBase64Compact()
}
override fun set(key: String, value: ByteArray?) {
val corrected = k(key)
if (value == null)
bst.removeItem(corrected)
else
bst.set(corrected, value.encodeToBase64Compact())
}
override val keys: Set<String>
get() {
val kk = mutableListOf<String>()
for (i in 0 until bst.length) {
val k = bst.key(i) ?: break
if( k.startsWith(prefix)) {
kk += k.substring(prefix.length)
}
}
return kk.toSet()
}
}

View File

@ -0,0 +1,27 @@
package net.sergeych.synctools
import kotlinx.atomicfu.locks.ReentrantLock
/**
* Get the platform-depended implementation of a mutex. It does nothing in the
* browser and use appropriate mechanics on JVM and native targets. See
* [ProtectedOpImplementation.invoke], [ProtectedOpImplementation.withLock]
* ```kotlin
* val op = ProtectedOp()
* //...
* op {
* // mutually exclusive execution
* println("sequential execution here")
* }
* ~~~
*/
actual fun ProtectedOp(): ProtectedOpImplementation = object: ProtectedOpImplementation {
val rlock = ReentrantLock()
override fun lock() {
rlock.lock()
}
override fun unlock() {
rlock.unlock()
}
}

View File

@ -0,0 +1,19 @@
package net.sergeych.synctools
/**
* Platform-independent interface to thread wait/notify. Does nothing in JS/browser,
* and uses appropriate mechanics on other platforms.
*
* Wasm is effictively songle threaded at the moment
*/
@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
actual class WaitHandle {
actual fun await(milliseconds: Long): Boolean {
// in JS we can't wait: no threads
return true
}
actual fun wakeUp() {
// in JS we can't wait: no threads
}
}