From 1b8af00d4e15f315d7fa8508b71ffdced122594d Mon Sep 17 00:00:00 2001 From: Darius Maitia Date: Wed, 11 Oct 2023 15:09:47 +0200 Subject: [PATCH] Kotlin multiplatform + Android support (#8) --- .github/workflows/ci.yml | 17 +- .github/workflows/release.yml | 19 ++- README.md | 140 ++++++++++++---- android-robot.png | Bin 0 -> 15192 bytes build.gradle.kts | 28 +++- examples/build.gradle.kts | 17 +- jvm.png | Bin 0 -> 30492 bytes kotlin-logo.png | Bin 0 -> 13116 bytes settings.gradle.kts | 8 + zenoh-kotlin/build.gradle.kts | 155 +++++++++++++++++- .../src/androidMain/AndroidManifest.xml | 2 + .../kotlin/io.zenoh}/Zenoh.kt | 2 +- .../kotlin/io/zenoh/Config.kt | 0 .../kotlin/io/zenoh/Logger.kt | 0 .../kotlin/io/zenoh/Resolvable.kt | 0 .../kotlin/io/zenoh/Session.kt | 0 .../kotlin/io/zenoh/SessionDeclaration.kt | 0 .../src/commonMain/kotlin/io/zenoh/Zenoh.kt | 21 +++ .../kotlin/io/zenoh/ZenohType.kt | 0 .../io/zenoh/exceptions/JNIException.kt | 0 .../io/zenoh/exceptions/KeyExprException.kt | 0 .../io/zenoh/exceptions/SessionException.kt | 0 .../kotlin/io/zenoh/handlers/Callback.kt | 0 .../io/zenoh/handlers/ChannelHandler.kt | 0 .../kotlin/io/zenoh/handlers/Handler.kt | 0 .../kotlin/io/zenoh/jni/JNIKeyExpr.kt | 0 .../kotlin/io/zenoh/jni/JNIPublisher.kt | 0 .../kotlin/io/zenoh/jni/JNIQuery.kt | 0 .../kotlin/io/zenoh/jni/JNIQueryable.kt | 0 .../kotlin/io/zenoh/jni/JNISession.kt | 0 .../kotlin/io/zenoh/jni/JNISubscriber.kt | 0 .../io/zenoh/jni/callbacks/JNIGetCallback.kt | 0 .../jni/callbacks/JNIQueryableCallback.kt | 0 .../jni/callbacks/JNISubscriberCallback.kt | 0 .../kotlin/io/zenoh/keyexpr/IntoKeyExpr.kt | 0 .../kotlin/io/zenoh/keyexpr/KeyExpr.kt | 0 .../kotlin/io/zenoh/prelude/Encoding.kt | 0 .../kotlin/io/zenoh/prelude/SampleKind.kt | 0 .../io/zenoh/publication/CongestionControl.kt | 0 .../kotlin/io/zenoh/publication/Delete.kt | 0 .../kotlin/io/zenoh/publication/Priority.kt | 0 .../kotlin/io/zenoh/publication/Publisher.kt | 0 .../kotlin/io/zenoh/publication/Put.kt | 0 .../io/zenoh/query/ConsolidationMode.kt | 0 .../kotlin/io/zenoh/query/Get.kt | 0 .../kotlin/io/zenoh/query/QueryTarget.kt | 0 .../kotlin/io/zenoh/query/Reply.kt | 0 .../kotlin/io/zenoh/queryable/Query.kt | 0 .../kotlin/io/zenoh/queryable/Queryable.kt | 0 .../kotlin/io/zenoh/sample/Sample.kt | 0 .../kotlin/io/zenoh/selector/IntoSelector.kt | 0 .../kotlin/io/zenoh/selector/Selector.kt | 0 .../kotlin/io/zenoh/subscriber/Reliability.kt | 0 .../kotlin/io/zenoh/subscriber/Subscriber.kt | 0 .../kotlin/io/zenoh/value/Value.kt | 0 .../kotlin/io/zenoh/DeleteTest.kt | 2 +- .../kotlin/io/zenoh/GetTest.kt | 15 +- .../kotlin/io/zenoh/KeyExprTest.kt | 4 +- .../kotlin/io/zenoh/PublisherTest.kt | 4 +- .../kotlin/io/zenoh/PutTest.kt | 2 +- .../kotlin/io/zenoh/QueryableTest.kt | 9 +- .../kotlin/io/zenoh/SelectorTest.kt | 2 +- .../kotlin/io/zenoh/SessionTest.kt | 4 +- .../kotlin/io/zenoh/SubscriberTest.kt | 5 +- .../src/jvmMain/kotlin/io/zenoh/Zenoh.kt | 77 +++++++++ 65 files changed, 442 insertions(+), 91 deletions(-) create mode 100644 android-robot.png create mode 100644 jvm.png create mode 100644 kotlin-logo.png create mode 100644 zenoh-kotlin/src/androidMain/AndroidManifest.xml rename zenoh-kotlin/src/{main/kotlin/io/zenoh => androidMain/kotlin/io.zenoh}/Zenoh.kt (95%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/Config.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/Logger.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/Resolvable.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/Session.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/SessionDeclaration.kt (100%) create mode 100644 zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Zenoh.kt rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/ZenohType.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/exceptions/JNIException.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/exceptions/KeyExprException.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/exceptions/SessionException.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/handlers/Callback.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/handlers/ChannelHandler.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/handlers/Handler.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/jni/JNIKeyExpr.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/jni/JNIPublisher.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/jni/JNIQuery.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/jni/JNIQueryable.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/jni/JNISession.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/jni/JNISubscriber.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/jni/callbacks/JNIGetCallback.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/jni/callbacks/JNIQueryableCallback.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/jni/callbacks/JNISubscriberCallback.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/keyexpr/IntoKeyExpr.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/keyexpr/KeyExpr.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/prelude/Encoding.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/prelude/SampleKind.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/publication/CongestionControl.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/publication/Delete.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/publication/Priority.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/publication/Publisher.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/publication/Put.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/query/ConsolidationMode.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/query/Get.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/query/QueryTarget.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/query/Reply.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/queryable/Query.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/queryable/Queryable.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/sample/Sample.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/selector/IntoSelector.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/selector/Selector.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/subscriber/Reliability.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/subscriber/Subscriber.kt (100%) rename zenoh-kotlin/src/{main => commonMain}/kotlin/io/zenoh/value/Value.kt (100%) rename zenoh-kotlin/src/{test => commonTest}/kotlin/io/zenoh/DeleteTest.kt (97%) rename zenoh-kotlin/src/{test => commonTest}/kotlin/io/zenoh/GetTest.kt (93%) rename zenoh-kotlin/src/{test => commonTest}/kotlin/io/zenoh/KeyExprTest.kt (96%) rename zenoh-kotlin/src/{test => commonTest}/kotlin/io/zenoh/PublisherTest.kt (97%) rename zenoh-kotlin/src/{test => commonTest}/kotlin/io/zenoh/PutTest.kt (97%) rename zenoh-kotlin/src/{test => commonTest}/kotlin/io/zenoh/QueryableTest.kt (94%) rename zenoh-kotlin/src/{test => commonTest}/kotlin/io/zenoh/SelectorTest.kt (95%) rename zenoh-kotlin/src/{test => commonTest}/kotlin/io/zenoh/SessionTest.kt (96%) rename zenoh-kotlin/src/{test => commonTest}/kotlin/io/zenoh/SubscriberTest.kt (97%) create mode 100644 zenoh-kotlin/src/jvmMain/kotlin/io/zenoh/Zenoh.kt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aceb5f2e..74c7833f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,14 @@ jobs: with: distribution: temurin java-version: 11 - + + - uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r26 + add-to-path: false + link-to-sdk: true + - name: Install Rust uses: actions-rs/toolchain@v1 with: @@ -53,9 +60,5 @@ jobs: working-directory: zenoh-jni run: cargo build --verbose - - name: Cargo Test - working-directory: zenoh-jni - run: cargo test --verbose -- --nocapture - - - name: Gradle Build - run: gradle build --scan + - name: Gradle Test + run: gradle jvmTest --info diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dd2f88d7..364bcd88 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,6 +27,13 @@ jobs: distribution: temurin java-version: 11 + - uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r26 + add-to-path: false + link-to-sdk: true + - name: Install Rust uses: actions-rs/toolchain@v1 with: @@ -48,12 +55,8 @@ jobs: working-directory: zenoh-jni run: cargo build --verbose - - name: Cargo Test - working-directory: zenoh-jni - run: cargo test --verbose -- --nocapture - - - name: Gradle Build - run: gradle build --scan + - name: Gradle Test + run: gradle jvmTest --info build_doc_and_deploy: name: Build and Deploy Documentation @@ -65,9 +68,7 @@ jobs: uses: gradle/gradle-build-action@v2 - name: Build doc - run: | - cd zenoh-kotlin - gradle dokkaHtml + run: gradle dokkaHtml - name: Deploy doc uses: peaceiris/actions-gh-pages@v3 diff --git a/README.md b/README.md index a500a921..be9688ad 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,15 @@ Zenoh (pronounce _/zeno/_) unifies data in motion, data at rest and computations Check the website [zenoh.io](http://zenoh.io) and the [roadmap](https://github.com/eclipse-zenoh/roadmap) for more detailed information. + ---- -# Kotlin API +# Kotlin Kotlin API This repository provides a Kotlin binding based on the main [Zenoh implementation written in Rust](https://github.com/eclipse-zenoh/zenoh). -The code relies on native code written in Rust and communicates with it via the Java Native Interface (JNI). +The code relies on the Zenoh JNI native library, which written in Rust and communicates with the Kotlin layer via the Java Native Interface (JNI). ## Documentation @@ -41,61 +42,113 @@ Basically: * Rust ([Installation guide](https://doc.rust-lang.org/cargo/getting-started/installation.html)) * Kotlin ([Installation guide](https://kotlinlang.org/docs/getting-started.html#backend)) * Gradle ([Installation guide](https://gradle.org/install/)) +* Android SDK ([Installation guide](https://developer.android.com/about/versions/11/setup-sdk)) -## Step by step +## Android Android -### Building zenoh-jni +In order to use these bindings in a native Android project, what we will do is to build them as an Android NDK Library, +publishing it into Maven local for us to be able to easily import it in our project. -Since Zenoh-Kotlin relies on a native rust interface that communicates with Zenoh, first you need to build it. +It is required to have the [NDK (native development kit)](https://developer.android.com/ndk) installed, since we are going to compile Zenoh JNI for multiple +android native targets. +It can be set up by using Android Studio (go to `Preferences > Appearance & Behavior > System settings > Android SDK > SDK Tools` and tick the NDK box), +or alternatively it can be found [here](https://developer.android.com/ndk/downloads). -Find the code in this repository on [here](/zenoh-jni): +The native platforms we are going to target are the following ones: +``` +- x86 +- x86_64 +- arm +- arm64 +``` +Therefore, if they are not yet already added to the Rust toolchain, run: ```bash -cd zenoh-jni +rustup target add armv7-linux-androideabi; \ +rustup target add i686-linux-android; \ +rustup target add aarch64-linux-android; \ +rustup target add x86_64-linux-android ``` -The let's build it with Cargo: +to install them. + +So, in order to publish the library onto Maven Local, run: ```bash -cargo build --release +gradle publishAndroidReleasePublicationToMavenLocal ``` -This will generate a library under `/target/release` named: -* MacOS: `libzenoh_jni.dylib` -* Linux: `libzenoh_jni.so` -* Windows: `libzenoh_jni.dll` +This will first trigger the compilation of the Zenoh-JNI for the previously mentioned targets, and secondly will +publish the library, containing the native binaries. -This file needs to be discoverable by the JVM. Therefore, `zenoh_jni` library should also be on the `java.library.path`. Thus depending on your -system make sure to install it at the proper place. +You should now be able to see the package under `~/.m2/repository/io/zenoh/zenoh-kotlin-android/0.10.0-rc` +with the following files: +``` +zenoh-kotlin-android-0.10.0-rc-sources.jar +zenoh-kotlin-android-0.10.0-rc.aar +zenoh-kotlin-android-0.10.0-rc.module +zenoh-kotlin-android-0.10.0-rc.pom +``` -For MacOS and Unix-like operating systems, the library is expected to be found under `/usr/local/lib`. -For Windows users you may want to add the location of the library to your `$PATH` environment variable. +Now the library is published on maven local, let's now see how to import it into an Android project. -:warning: Note that failure to make `zenoh_jni` discoverable will cause the kotlin tests fail during the kotlin build process and -any further intent to use this library will result in an error during runtime, due to an `UnsatisfiedLinkError`. +First, we need to indicate we want to look into mavenLocal for our library, so in your top level `build.gradle.kts` you need to specify +the `mavenLocal` repository: +```agsl +repositories { + mavenCentral() + ... + mavenLocal() // We add this line +} +``` -### Building Kotlin! +Then in your app's `build.gradle.kts` filen add the dependency: +``` +implementation("io.zenoh:zenoh-kotlin-android:0.10.0-rc") +``` -Now let's go to the [zenoh-kotlin subdirectory](zenoh-kotlin) of this repository. +And finally, do not forget to add the required internet permissions on your manifest! -```bash -cd zenoh-kotlin +``` + + ``` +And that was it! You can now import the code from the `io.zenoh` package and use it at your will. -It is best to build and run using the `gradle` wrapper, thus type: +## JVM JVM - $ gradle wrapper +To publish a library for a JVM project into Maven local, run + +```bash +gradle publishJvmPublicationToMavenLocal +``` -Then you can build by simply: +This will first, trigger the compilation of Zenoh-JNI, and second publish the library into maven local, containing the native library +as a resource that will be loaded during runtime. - $ ./gradlew build +:warning: The native library will be compiled against the default rustup target on your machine, so although it may work fine +for you on your desktop, the generated publication may not be working on another computer with a different operating system and/or a different cpu architecture. +This is different from Android in the fact that Android provides an in build mechanism to dynamically load native libraries depending on the CPU's architecture, while +for JVM it's not the case and that logic must be implemented. Building against multiple targets and loading them dynamically is one of our short term goals. +Once we have published the package, we should be able to find it under `~/.m2/repository/io/zenoh/zenoh-kotlin-jvm/0.10.0-rc`. +Finally, in the `build.gradle.kts` file of the project where you intend to use this library, add mavenLocal to the list of repositories and add zenoh-kotlin as a dependency: -That was it! We now can build our first Kotlin app using Zenoh! +``` +repositories { + mavenCentral() + mavenLocal() +} + +dependencies { + testImplementation(kotlin("test")) + implementation("io.zenoh:zenoh-kotlin-jvm:0.10.0-rc") +} +``` -### Building the documentation +## Building the documentation Because it's a Kotlin project, we use [Dokka](https://kotlinlang.org/docs/dokka-introduction.html) to generate the documentation. @@ -104,6 +157,31 @@ In order to build it, run: gradle zenoh-kotlin:dokkaHtml ``` +## Running the tests + +To run the tests, run: + +```bash +gradle jvmTest +``` + +This will compile the native library on debug mode (if not already available) and run the tests afterward against the JVM target. +Running the tests against the Android target (by using `gradle testDebugUnitTest`) is equivalent to running them against the JVM one, since they are common +tests executed locally as Unit tests. + +## Logging + +Rust logs are propagated when setting the property `zenoh.logger=debug` (using RUST_LOG=debug will result in nothing) + +For instance running the ZPub test as follows: + +```bash +gradle -Pzenoh.logger=debug ZPub +``` + +causes the logs to appear in standard output. + +The log levels are the ones from Rust: `trace`, `info`, `debug`, `error` and `warn`. --- @@ -120,6 +198,10 @@ For instance in order to run the [ZPub](examples/src/main/kotlin/io.zenoh/ZPub.k You can find more info about these examples on the [examples README file](/examples/README.md). + + + + ---- # :warning: Considerations & Future work diff --git a/android-robot.png b/android-robot.png new file mode 100644 index 0000000000000000000000000000000000000000..162313dae0a3391666b0f0a2756f33d7fb02e6fa GIT binary patch literal 15192 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 D2N6O< literal 0 HcmV?d00001 diff --git a/build.gradle.kts b/build.gradle.kts index 072f5bcf..ab3c317b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,20 +12,30 @@ // ZettaScale Zenoh Team, // + +buildscript { + repositories { + google() + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0") + classpath("org.mozilla.rust-android-gradle:plugin:0.9.3") + classpath("com.android.tools.build:gradle:7.4.2") + } +} + plugins { - kotlin("jvm") version "1.9.0" - id("org.jetbrains.dokka") version "1.8.20" + id("com.android.library") version "7.4.2" apply false + id("org.jetbrains.kotlin.android") version "1.9.10" apply false + id("org.jetbrains.kotlin.multiplatform") version "1.9.0" apply false + id("org.mozilla.rust-android-gradle.rust-android") version "0.9.3" apply false + id("org.jetbrains.dokka") version "1.8.20" apply false + id("com.adarshr.test-logger") version "3.2.0" apply false } subprojects { - apply(plugin = "kotlin") - apply(plugin = "org.jetbrains.dokka") - - kotlin { - jvmToolchain(11) - } - repositories { + google() mavenCentral() } } diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 12698a2c..3b1eadff 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -15,6 +15,14 @@ group = "io.zenoh" version = "0.11.0-dev" +plugins { + kotlin("jvm") +} + +kotlin { + jvmToolchain(11) +} + dependencies { implementation(project(":zenoh-kotlin")) implementation("commons-net:commons-net:3.9.0") @@ -35,10 +43,11 @@ tasks { examples.forEach { example -> register(example, JavaExec::class) { + dependsOn("CompileZenohJNI") description = "Run the $example example" mainClass.set("io.zenoh.${example}Kt") classpath(sourceSets["main"].runtimeClasspath) - val zenohPaths = "/usr/local/lib:../zenoh-jni/target/release" + val zenohPaths = "../zenoh-jni/target/release" val defaultJvmArgs = arrayListOf("-Djava.library.path=$zenohPaths") val loggerLvl = project.findProperty("zenoh.logger")?.toString() if (loggerLvl != null) { @@ -49,3 +58,9 @@ tasks { } } } + +tasks.register("CompileZenohJNI") { + project.exec { + commandLine("cargo", "build", "--release", "--manifest-path", "../zenoh-jni/Cargo.toml") + } +} diff --git a/jvm.png b/jvm.png new file mode 100644 index 0000000000000000000000000000000000000000..07f1b123cc01049d101042e1244e36bcc142424d GIT binary patch literal 30492 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 v!O2`lC2~;$%D)aOiJ48(_#OH`3%vwj literal 0 HcmV?d00001 diff --git a/settings.gradle.kts b/settings.gradle.kts index 16eef7dc..6126e8ce 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,10 +12,18 @@ // ZettaScale Zenoh Team, // +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + google() + } +} rootProject.name = "zenoh-kotlin" include(":zenoh-kotlin") include(":examples") +include(":zenoh-jni") plugins { id("org.gradle.toolchains.foojay-resolver-convention") version("0.4.0") diff --git a/zenoh-kotlin/build.gradle.kts b/zenoh-kotlin/build.gradle.kts index 3fe849b1..91486dde 100644 --- a/zenoh-kotlin/build.gradle.kts +++ b/zenoh-kotlin/build.gradle.kts @@ -16,17 +16,154 @@ group = "io.zenoh" version = "0.11.0-dev" plugins { - id("com.adarshr.test-logger") version "3.2.0" + id("com.android.library") + kotlin("multiplatform") + id("com.adarshr.test-logger") + id("org.jetbrains.dokka") + id("org.mozilla.rust-android-gradle.rust-android") + `maven-publish` } -dependencies { - testImplementation(kotlin("test")) - implementation("commons-net:commons-net:3.9.0") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") +android { + namespace = "io.zenoh" + compileSdk = 30 + + ndkVersion = "26.0.10792818" + + defaultConfig { + minSdk = 30 + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + buildTypes { + getByName("release") { + isMinifyEnabled = false + } + getByName("debug") { + isMinifyEnabled = false + } + } + sourceSets { + getByName("main") { + manifest.srcFile("src/androidMain/AndroidManifest.xml") + } + } + publishing { + singleVariant("release") { + withSourcesJar() + withJavadocJar() + } + } +} + +cargo { + pythonCommand = "python3" + module = "../zenoh-jni" + libname = "zenoh-jni" + targetIncludes = arrayOf("libzenoh_jni.so") + targetDirectory = "../zenoh-jni/target/" + profile = "release" + targets = arrayListOf( + "arm", + "arm64", + "x86", + "x86_64", + ) +} + +kotlin { + jvmToolchain(11) + jvm { + compilations.all { + kotlinOptions.jvmTarget = "11" + } + testRuns["test"].executionTask.configure { + val zenohPaths = "../zenoh-jni/target/release:../zenoh-jni/target/debug" + jvmArgs("-Djava.library.path=$zenohPaths") + } + } + androidTarget { + publishLibraryVariants("release") + } + + @Suppress("Unused") sourceSets { + val commonMain by getting { + dependencies { + implementation("commons-net:commons-net:3.9.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + } + } + val androidUnitTest by getting { + dependencies { + implementation(kotlin("test-junit")) + } + } + val jvmMain by getting { + resources.srcDir("../zenoh-jni/target/release").include(arrayListOf("*.dylib", "*.so", "*.dll")) + } + val jvmTest by getting { + resources.srcDir("../zenoh-jni/target/debug").include(arrayListOf("*.dylib", "*.so", "*.dll")) + } + } +} + +tasks.withType { + doFirst { + buildZenohJNI(BuildMode.DEBUG) + + // The line below is added for the Android Unit tests which are equivalent to the JVM tests. + // For them to work we need to specify the path to the native library as a system property and not as a jvmArg. + systemProperty("java.library.path", "../zenoh-jni/target/debug") + } +} + +tasks.whenObjectAdded { + if ((this.name == "mergeDebugJniLibFolders" || this.name == "mergeReleaseJniLibFolders")) { + this.dependsOn("cargoBuild") + this.inputs.dir(buildDir.resolve("rustJniLibs/android")) + } +} + +tasks.named("compileKotlinJvm") { + doFirst { + buildZenohJNI(BuildMode.RELEASE) + } +} + +fun buildZenohJNI(mode: BuildMode = BuildMode.DEBUG) { + val cargoCommand = mutableListOf("cargo", "build") + + if (mode == BuildMode.RELEASE) { + cargoCommand.add("--release") + } + + val result = project.exec { + commandLine(*(cargoCommand.toTypedArray()), "--manifest-path", "../zenoh-jni/Cargo.toml") + } + + if (result.exitValue != 0) { + throw GradleException("Failed to build Zenoh-JNI.") + } } -tasks.test { - useJUnitPlatform() - val zenohPaths = "/usr/local/lib:../zenoh-jni/target/release:../zenoh-jni/target/debug" - jvmArgs("-Djava.library.path=$zenohPaths") +enum class BuildMode { + DEBUG { + override fun toString(): String { + return "debug" + } + }, + RELEASE { + override fun toString(): String { + return "release" + } + } } diff --git a/zenoh-kotlin/src/androidMain/AndroidManifest.xml b/zenoh-kotlin/src/androidMain/AndroidManifest.xml new file mode 100644 index 00000000..4fb03756 --- /dev/null +++ b/zenoh-kotlin/src/androidMain/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/Zenoh.kt b/zenoh-kotlin/src/androidMain/kotlin/io.zenoh/Zenoh.kt similarity index 95% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/Zenoh.kt rename to zenoh-kotlin/src/androidMain/kotlin/io.zenoh/Zenoh.kt index c2d32a31..dd9d3567 100644 --- a/zenoh-kotlin/src/main/kotlin/io/zenoh/Zenoh.kt +++ b/zenoh-kotlin/src/androidMain/kotlin/io.zenoh/Zenoh.kt @@ -18,7 +18,7 @@ package io.zenoh * Static singleton class to load the Zenoh native library once and only once, as well as the logger in function of the * log level configuration. */ -internal class Zenoh private constructor() { +internal actual class Zenoh private actual constructor() { companion object { private const val ZENOH_LIB_NAME = "zenoh_jni" diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/Config.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Config.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/Config.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Config.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/Logger.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Logger.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/Logger.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Logger.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/Resolvable.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Resolvable.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/Resolvable.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Resolvable.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/Session.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Session.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/Session.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Session.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/SessionDeclaration.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/SessionDeclaration.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/SessionDeclaration.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/SessionDeclaration.kt diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Zenoh.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Zenoh.kt new file mode 100644 index 00000000..ef6bebbc --- /dev/null +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Zenoh.kt @@ -0,0 +1,21 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// + +package io.zenoh + +/** + * Static singleton class to load the Zenoh native library once and only once, as well as the logger in function of the + * log level configuration. + */ +internal expect class Zenoh private constructor() diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/ZenohType.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/ZenohType.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/ZenohType.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/ZenohType.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/exceptions/JNIException.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/exceptions/JNIException.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/exceptions/JNIException.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/exceptions/JNIException.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/exceptions/KeyExprException.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/exceptions/KeyExprException.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/exceptions/KeyExprException.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/exceptions/KeyExprException.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/exceptions/SessionException.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/exceptions/SessionException.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/exceptions/SessionException.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/exceptions/SessionException.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/handlers/Callback.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/handlers/Callback.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/handlers/Callback.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/handlers/Callback.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/handlers/ChannelHandler.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/handlers/ChannelHandler.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/handlers/ChannelHandler.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/handlers/ChannelHandler.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/handlers/Handler.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/handlers/Handler.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/handlers/Handler.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/handlers/Handler.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/jni/JNIKeyExpr.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/jni/JNIKeyExpr.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/jni/JNIPublisher.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIPublisher.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/jni/JNIPublisher.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIPublisher.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/jni/JNIQuery.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIQuery.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/jni/JNIQuery.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIQuery.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/jni/JNIQueryable.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIQueryable.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/jni/JNIQueryable.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIQueryable.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/jni/JNISession.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNISession.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/jni/JNISession.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNISession.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/jni/JNISubscriber.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNISubscriber.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/jni/JNISubscriber.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNISubscriber.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/jni/callbacks/JNIGetCallback.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIGetCallback.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/jni/callbacks/JNIGetCallback.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIGetCallback.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/jni/callbacks/JNIQueryableCallback.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIQueryableCallback.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/jni/callbacks/JNIQueryableCallback.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIQueryableCallback.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/jni/callbacks/JNISubscriberCallback.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNISubscriberCallback.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/jni/callbacks/JNISubscriberCallback.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNISubscriberCallback.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/keyexpr/IntoKeyExpr.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/IntoKeyExpr.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/keyexpr/IntoKeyExpr.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/IntoKeyExpr.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/keyexpr/KeyExpr.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/keyexpr/KeyExpr.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/prelude/Encoding.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/prelude/Encoding.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/prelude/Encoding.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/prelude/Encoding.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/prelude/SampleKind.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/prelude/SampleKind.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/prelude/SampleKind.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/prelude/SampleKind.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/publication/CongestionControl.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/publication/CongestionControl.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/publication/CongestionControl.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/publication/CongestionControl.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/publication/Delete.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/publication/Delete.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/publication/Delete.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/publication/Delete.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/publication/Priority.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/publication/Priority.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/publication/Priority.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/publication/Priority.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/publication/Publisher.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/publication/Publisher.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/publication/Publisher.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/publication/Publisher.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/publication/Put.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/publication/Put.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/publication/Put.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/publication/Put.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/query/ConsolidationMode.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/ConsolidationMode.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/query/ConsolidationMode.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/ConsolidationMode.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/query/Get.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/Get.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/query/Get.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/Get.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/query/QueryTarget.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/QueryTarget.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/query/QueryTarget.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/QueryTarget.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/query/Reply.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/Reply.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/query/Reply.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/Reply.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/queryable/Query.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/queryable/Query.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/queryable/Query.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/queryable/Query.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/queryable/Queryable.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/queryable/Queryable.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/queryable/Queryable.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/queryable/Queryable.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/sample/Sample.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/sample/Sample.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/sample/Sample.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/sample/Sample.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/selector/IntoSelector.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/selector/IntoSelector.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/selector/IntoSelector.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/selector/IntoSelector.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/selector/Selector.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/selector/Selector.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/selector/Selector.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/selector/Selector.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/subscriber/Reliability.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/subscriber/Reliability.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/subscriber/Reliability.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/subscriber/Reliability.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/subscriber/Subscriber.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/subscriber/Subscriber.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/subscriber/Subscriber.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/subscriber/Subscriber.kt diff --git a/zenoh-kotlin/src/main/kotlin/io/zenoh/value/Value.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/value/Value.kt similarity index 100% rename from zenoh-kotlin/src/main/kotlin/io/zenoh/value/Value.kt rename to zenoh-kotlin/src/commonMain/kotlin/io/zenoh/value/Value.kt diff --git a/zenoh-kotlin/src/test/kotlin/io/zenoh/DeleteTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/DeleteTest.kt similarity index 97% rename from zenoh-kotlin/src/test/kotlin/io/zenoh/DeleteTest.kt rename to zenoh-kotlin/src/commonTest/kotlin/io/zenoh/DeleteTest.kt index b16ef692..82140552 100644 --- a/zenoh-kotlin/src/test/kotlin/io/zenoh/DeleteTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/DeleteTest.kt @@ -17,7 +17,7 @@ package io.zenoh import io.zenoh.keyexpr.intoKeyExpr import io.zenoh.prelude.SampleKind import io.zenoh.sample.Sample -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull diff --git a/zenoh-kotlin/src/test/kotlin/io/zenoh/GetTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/GetTest.kt similarity index 93% rename from zenoh-kotlin/src/test/kotlin/io/zenoh/GetTest.kt rename to zenoh-kotlin/src/commonTest/kotlin/io/zenoh/GetTest.kt index 040ae5e5..7c9f2954 100644 --- a/zenoh-kotlin/src/test/kotlin/io/zenoh/GetTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/GetTest.kt @@ -27,11 +27,11 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.apache.commons.net.ntp.TimeStamp -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test import java.time.Duration import java.util.* import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.test.Test class GetTest { @@ -60,12 +60,11 @@ class GetTest { val sessionB = Session.open().getOrThrow() sessionB.get(keyExpr).with { reply: Reply -> - Assertions.assertTrue(reply is Reply.Success) - val receivedSample = (reply as Reply.Success).sample - Assertions.assertEquals(value, receivedSample.value) - Assertions.assertEquals(kind, receivedSample.kind) - Assertions.assertEquals(keyExpr, receivedSample.keyExpr) - Assertions.assertEquals(timeStamp, receivedSample.timestamp) + assertTrue(reply is Reply.Success) + assertEquals(value, reply.sample.value) + assertEquals(kind, reply.sample.kind) + assertEquals(keyExpr, reply.sample.keyExpr) + assertEquals(timeStamp, reply.sample.timestamp) }.timeout(Duration.ofMillis(1000)).res() Thread.sleep(1000) diff --git a/zenoh-kotlin/src/test/kotlin/io/zenoh/KeyExprTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt similarity index 96% rename from zenoh-kotlin/src/test/kotlin/io/zenoh/KeyExprTest.kt rename to zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt index 8855e3aa..2cb36495 100644 --- a/zenoh-kotlin/src/test/kotlin/io/zenoh/KeyExprTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt @@ -17,8 +17,6 @@ package io.zenoh import io.zenoh.exceptions.SessionException import io.zenoh.keyexpr.KeyExpr import io.zenoh.keyexpr.intoKeyExpr -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows import kotlin.test.* class KeyExprTest { @@ -124,7 +122,7 @@ class KeyExprTest { val keyExpr2 = "x/y/z".intoKeyExpr().getOrThrow() val undeclare2 = session.undeclare(keyExpr2).res() assertTrue(undeclare2.isFailure) - assertThrows { undeclare2.getOrThrow() } + assertTrue(undeclare2.exceptionOrNull() is SessionException) session.close() keyExpr.close() diff --git a/zenoh-kotlin/src/test/kotlin/io/zenoh/PublisherTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/PublisherTest.kt similarity index 97% rename from zenoh-kotlin/src/test/kotlin/io/zenoh/PublisherTest.kt rename to zenoh-kotlin/src/commonTest/kotlin/io/zenoh/PublisherTest.kt index 614cb136..16c6b403 100644 --- a/zenoh-kotlin/src/test/kotlin/io/zenoh/PublisherTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/PublisherTest.kt @@ -20,8 +20,8 @@ import io.zenoh.prelude.Encoding import io.zenoh.prelude.SampleKind import io.zenoh.sample.Sample import io.zenoh.value.Value -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test +import kotlin.test.Test +import kotlin.test.assertEquals class PublisherTest { diff --git a/zenoh-kotlin/src/test/kotlin/io/zenoh/PutTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/PutTest.kt similarity index 97% rename from zenoh-kotlin/src/test/kotlin/io/zenoh/PutTest.kt rename to zenoh-kotlin/src/commonTest/kotlin/io/zenoh/PutTest.kt index 91fa32ec..d7dd7134 100644 --- a/zenoh-kotlin/src/test/kotlin/io/zenoh/PutTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/PutTest.kt @@ -19,7 +19,7 @@ import io.zenoh.keyexpr.intoKeyExpr import io.zenoh.prelude.Encoding import io.zenoh.sample.Sample import io.zenoh.value.Value -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull diff --git a/zenoh-kotlin/src/test/kotlin/io/zenoh/QueryableTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/QueryableTest.kt similarity index 94% rename from zenoh-kotlin/src/test/kotlin/io/zenoh/QueryableTest.kt rename to zenoh-kotlin/src/commonTest/kotlin/io/zenoh/QueryableTest.kt index 4e769d7e..dad943e5 100644 --- a/zenoh-kotlin/src/test/kotlin/io/zenoh/QueryableTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/QueryableTest.kt @@ -23,14 +23,14 @@ import io.zenoh.sample.Sample import io.zenoh.value.Value import kotlinx.coroutines.channels.Channel import org.apache.commons.net.ntp.TimeStamp -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test import java.time.Duration import java.time.Instant import java.util.* +import kotlin.test.Test +import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull +import kotlin.test.assertTrue class QueryableTest { @@ -55,8 +55,7 @@ class QueryableTest { sessionB.get(TEST_KEY_EXP).with { reply: Reply -> assertTrue(reply is Reply.Success) - val receivedSample = (reply as Reply.Success).sample - assertEquals(receivedSample, sample) + assertEquals(reply.sample, sample) }.timeout(Duration.ofMillis(1000)).res() Thread.sleep(1000) diff --git a/zenoh-kotlin/src/test/kotlin/io/zenoh/SelectorTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/SelectorTest.kt similarity index 95% rename from zenoh-kotlin/src/test/kotlin/io/zenoh/SelectorTest.kt rename to zenoh-kotlin/src/commonTest/kotlin/io/zenoh/SelectorTest.kt index 6a5d278a..0a579563 100644 --- a/zenoh-kotlin/src/test/kotlin/io/zenoh/SelectorTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/SelectorTest.kt @@ -3,7 +3,7 @@ package io.zenoh import io.zenoh.exceptions.KeyExprException import io.zenoh.selector.Selector import io.zenoh.selector.intoSelector -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith diff --git a/zenoh-kotlin/src/test/kotlin/io/zenoh/SessionTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/SessionTest.kt similarity index 96% rename from zenoh-kotlin/src/test/kotlin/io/zenoh/SessionTest.kt rename to zenoh-kotlin/src/commonTest/kotlin/io/zenoh/SessionTest.kt index 4d749b8c..f74e97c1 100644 --- a/zenoh-kotlin/src/test/kotlin/io/zenoh/SessionTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/SessionTest.kt @@ -17,9 +17,7 @@ package io.zenoh import io.zenoh.exceptions.SessionException import io.zenoh.keyexpr.intoKeyExpr import io.zenoh.sample.Sample -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.Test -import kotlin.test.assertFailsWith +import kotlin.test.* class SessionTest { diff --git a/zenoh-kotlin/src/test/kotlin/io/zenoh/SubscriberTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/SubscriberTest.kt similarity index 97% rename from zenoh-kotlin/src/test/kotlin/io/zenoh/SubscriberTest.kt rename to zenoh-kotlin/src/commonTest/kotlin/io/zenoh/SubscriberTest.kt index c797b4ed..7c96d4c4 100644 --- a/zenoh-kotlin/src/test/kotlin/io/zenoh/SubscriberTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/SubscriberTest.kt @@ -21,9 +21,10 @@ import io.zenoh.prelude.Encoding import io.zenoh.sample.Sample import io.zenoh.value.Value import kotlinx.coroutines.channels.Channel -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.Test import kotlin.collections.ArrayList +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.test.Test class SubscriberTest { diff --git a/zenoh-kotlin/src/jvmMain/kotlin/io/zenoh/Zenoh.kt b/zenoh-kotlin/src/jvmMain/kotlin/io/zenoh/Zenoh.kt new file mode 100644 index 00000000..0515c1a6 --- /dev/null +++ b/zenoh-kotlin/src/jvmMain/kotlin/io/zenoh/Zenoh.kt @@ -0,0 +1,77 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// + +package io.zenoh + +import java.io.File +import java.io.FileOutputStream +import java.io.InputStream +import java.lang.Exception + +/** + * Static singleton class to load the Zenoh native library once and only once, as well as the logger in function of the + * log level configuration. + */ +internal actual class Zenoh private actual constructor() { + + companion object { + private const val ZENOH_LIB_NAME = "zenoh_jni" + private const val ZENOH_LOGS_PROPERTY = "zenoh.logger" + + private var instance: Zenoh? = null + + fun load() { + instance ?: Zenoh().also { instance = it } + } + + fun loadZenohJNI(inputStream: InputStream) { + val tempLib = File.createTempFile("tempLib", "") + tempLib.deleteOnExit() + + FileOutputStream(tempLib).use { output -> + inputStream.copyTo(output) + } + + System.load(tempLib.absolutePath) + } + } + + init { + val lib = ClassLoader.getSystemClassLoader().findLibraryStream(ZENOH_LIB_NAME) + + if (lib != null) { + loadZenohJNI(lib) + } else { + throw Exception("Unable to load ZenohJNI.") + } + + val logLevel = System.getProperty(ZENOH_LOGS_PROPERTY) + if (logLevel != null) { + Logger.start(logLevel) + } + } +} + +private fun ClassLoader.findLibraryStream(libraryName: String): InputStream? { + // TODO: look after targets of multiple architectures + val libraryExtensions = listOf(".dylib", ".so", ".dll") + for (extension in libraryExtensions) { + val resourcePath = "lib$libraryName$extension" + val inputStream = getResourceAsStream(resourcePath) + if (inputStream != null) { + return inputStream + } + } + return null +}