From c5d1d3b31e65bd87e64d28405785a6bde24ee947 Mon Sep 17 00:00:00 2001 From: Patrick Goldinger Date: Fri, 5 Apr 2024 01:24:10 +0200 Subject: [PATCH] Add Rust build support --- .gitignore | 10 +++ app/build.gradle.kts | 13 +--- .../florisboard/FlorisApplication.kt | 35 +-------- lib/native/build.gradle.kts | 71 +++++++++++++++++++ lib/native/src/main/AndroidManifest.xml | 4 ++ .../kotlin/org/florisboard/libnative/test.kt | 3 + lib/native/src/main/rust/CMakeLists.txt | 53 ++++++++++++++ lib/native/src/main/rust/Cargo.toml | 12 ++++ lib/native/src/main/rust/src/lib.c | 1 + lib/native/src/main/rust/src/lib.rs | 15 ++++ libnative/dummy/Cargo.toml | 6 ++ libnative/dummy/src/lib.rs | 3 + settings.gradle.kts | 1 + 13 files changed, 183 insertions(+), 44 deletions(-) create mode 100644 lib/native/build.gradle.kts create mode 100644 lib/native/src/main/AndroidManifest.xml create mode 100644 lib/native/src/main/kotlin/org/florisboard/libnative/test.kt create mode 100644 lib/native/src/main/rust/CMakeLists.txt create mode 100644 lib/native/src/main/rust/Cargo.toml create mode 100644 lib/native/src/main/rust/src/lib.c create mode 100644 lib/native/src/main/rust/src/lib.rs create mode 100644 libnative/dummy/Cargo.toml create mode 100644 libnative/dummy/src/lib.rs diff --git a/.gitignore b/.gitignore index 4670104f..8b5b036d 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,13 @@ crowdin.properties # Nix stuff .direnv/ result + +# VSCode +.vscode/ + +# Rust +debug/ +target/ +Cargo.lock +**/*.rs.bk +*.pdb diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f9e5c9ba..02809a4a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -101,12 +101,6 @@ android { kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() } - //externalNativeBuild { - // cmake { - // path("src/main/cpp/CMakeLists.txt") - // } - //} - buildTypes { named("debug") { applicationIdSuffix = ".debug" @@ -152,11 +146,6 @@ android { initWith(getByName("release")) signingConfig = signingConfigs.getByName("debug") matchingFallbacks += listOf("release") - - ndk { - // For running FlorisBoard on the emulator - abiFilters += listOf("x86", "x86_64") - } } } @@ -214,8 +203,8 @@ dependencies { implementation(libs.patrickgold.jetpref.datastore.ui) implementation(libs.patrickgold.jetpref.material.ui) - implementation(project(":lib:kotlin")) + implementation(project(":lib:native")) testImplementation(libs.equalsverifier) testImplementation(libs.kotest.assertions.core) diff --git a/app/src/main/kotlin/dev/patrickgold/florisboard/FlorisApplication.kt b/app/src/main/kotlin/dev/patrickgold/florisboard/FlorisApplication.kt index 4134157e..471eeeef 100644 --- a/app/src/main/kotlin/dev/patrickgold/florisboard/FlorisApplication.kt +++ b/app/src/main/kotlin/dev/patrickgold/florisboard/FlorisApplication.kt @@ -34,20 +34,17 @@ import dev.patrickgold.florisboard.ime.media.emoji.FlorisEmojiCompat import dev.patrickgold.florisboard.ime.nlp.NlpManager import dev.patrickgold.florisboard.ime.text.gestures.GlideTypingManager import dev.patrickgold.florisboard.ime.theme.ThemeManager -import dev.patrickgold.florisboard.lib.NativeStr import dev.patrickgold.florisboard.lib.cache.CacheManager import dev.patrickgold.florisboard.lib.crashutility.CrashUtility import dev.patrickgold.florisboard.lib.devtools.Flog import dev.patrickgold.florisboard.lib.devtools.LogTopic import dev.patrickgold.florisboard.lib.devtools.flogError -import dev.patrickgold.florisboard.lib.devtools.flogInfo import dev.patrickgold.florisboard.lib.ext.ExtensionManager import dev.patrickgold.florisboard.lib.io.AssetManager import dev.patrickgold.florisboard.lib.io.deleteContentsRecursively -import dev.patrickgold.florisboard.lib.io.subFile -import dev.patrickgold.florisboard.lib.toNativeStr import dev.patrickgold.jetpref.datastore.JetPref import org.florisboard.lib.kotlin.tryOrNull +import org.florisboard.libnative.dummyAdd import java.lang.ref.WeakReference /** @@ -59,13 +56,9 @@ private var FlorisApplicationReference = WeakReference(null) @Suppress("unused") class FlorisApplication : Application() { companion object { - private const val ICU_DATA_ASSET_PATH = "icu4c/icudt73l.dat" - - private external fun nativeInitICUData(path: NativeStr): Int - init { try { - System.loadLibrary("florisboard-native") + System.loadLibrary("fl_native") } catch (_: Exception) { } } @@ -99,6 +92,7 @@ class FlorisApplication : Application() { ) CrashUtility.install(this) FlorisEmojiCompat.init(this) + flogError { "dummy result: ${dummyAdd(3,4)}" } if (!UserManagerCompat.isUserUnlocked(this)) { cacheDir?.deleteContentsRecursively() @@ -115,7 +109,6 @@ class FlorisApplication : Application() { } fun init() { - initICU(this) cacheDir?.deleteContentsRecursively() prefs.initializeBlocking(this) extensionManager.value.init() @@ -123,28 +116,6 @@ class FlorisApplication : Application() { DictionaryManager.init(this) } - fun initICU(context: Context): Boolean { - try { - val androidAssetManager = context.assets ?: return false - val icuTmpDataFile = context.cacheDir.subFile("icudt.dat") - icuTmpDataFile.outputStream().use { os -> - androidAssetManager.open(ICU_DATA_ASSET_PATH).use { it.copyTo(os) } - } - val status = nativeInitICUData(icuTmpDataFile.absolutePath.toNativeStr()) - icuTmpDataFile.delete() - return if (status != 0) { - flogError { "Native ICU data initializing failed with error code $status!" } - false - } else { - flogInfo { "Successfully loaded ICU data!" } - true - } - } catch (e: Exception) { - flogError { e.toString() } - return false - } - } - private inner class BootComplete : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent == null) return diff --git a/lib/native/build.gradle.kts b/lib/native/build.gradle.kts new file mode 100644 index 00000000..1be5c7ef --- /dev/null +++ b/lib/native/build.gradle.kts @@ -0,0 +1,71 @@ +plugins { + alias(libs.plugins.agp.library) + alias(libs.plugins.kotlin.android) +} + +val projectMinSdk: String by project +val projectCompileSdk: String by project +val projectNdkVersion: String by project + +android { + namespace = "org.florisboard.libnative" + compileSdk = projectCompileSdk.toInt() + ndkVersion = projectNdkVersion + + defaultConfig { + minSdk = projectMinSdk.toInt() + + externalNativeBuild { + cmake { + targets("fl_native") + arguments( + "-DCMAKE_ANDROID_API=" + minSdk.toString(), + ) + } + } + + ndk { + //abiFilters += listOf("armeabi-v7a", "arm64-v8a") + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + + sourceSets { + maybeCreate("main").apply { + java { + srcDirs("src/main/kotlin") + } + } + } + + externalNativeBuild { + cmake { + path("src/main/rust/CMakeLists.txt") + } + } +} + +tasks.named("clean") { + doLast { + delete("src/main/rust/target", "src/main/rust/Cargo.lock") + } +} + +dependencies { + // none +} diff --git a/lib/native/src/main/AndroidManifest.xml b/lib/native/src/main/AndroidManifest.xml new file mode 100644 index 00000000..8bdb7e14 --- /dev/null +++ b/lib/native/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/lib/native/src/main/kotlin/org/florisboard/libnative/test.kt b/lib/native/src/main/kotlin/org/florisboard/libnative/test.kt new file mode 100644 index 00000000..6834c256 --- /dev/null +++ b/lib/native/src/main/kotlin/org/florisboard/libnative/test.kt @@ -0,0 +1,3 @@ +package org.florisboard.libnative + +external fun dummyAdd(a: Int, b: Int): Int diff --git a/lib/native/src/main/rust/CMakeLists.txt b/lib/native/src/main/rust/CMakeLists.txt new file mode 100644 index 00000000..bf49ac90 --- /dev/null +++ b/lib/native/src/main/rust/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.22) + +project(florisboard) + +if(WIN32) + set(USER_HOME_DIRECTORY "$ENV{USERPROFILE}") +else() + set(USER_HOME_DIRECTORY "$ENV{HOME}") +endif() +set(RUST_TOOLCHAIN "${USER_HOME_DIRECTORY}/.cargo/bin") + +### FlorisBoard ### + +add_library(fl_native SHARED src/lib.c) + +if(CMAKE_ANDROID_ARCH_ABI STREQUAL "armeabi-v7a") + set(ANDROID_TARGET "armv7a-linux-androideabi") + set(RUST_TARGET "armv7-linux-androideabi") +elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") + set(ANDROID_TARGET "aarch64-linux-android") + set(RUST_TARGET "aarch64-linux-android") +elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "x86") + set(ANDROID_TARGET "i686-linux-android") + set(RUST_TARGET "i686-linux-android") +elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64") + set(ANDROID_TARGET "x86_64-linux-android") + set(RUST_TARGET "x86_64-linux-android") +else() + message(FATAL_ERROR "Unsupported ABI: ${CMAKE_ANDROID_ARCH_ABI}") +endif() +get_filename_component(LLVM_TOOLCHAIN ${CMAKE_C_COMPILER} DIRECTORY) + +set(FL_NATIVE_RUST_PATH "${CMAKE_CURRENT_SOURCE_DIR}/target/${RUST_TARGET}/release/libfl_native_rust.a") + +add_custom_target( + setup_rust_target ALL + COMMAND ${RUST_TOOLCHAIN}/rustup target add ${RUST_TARGET} +) +add_custom_target( + fl_native_rust_build ALL + COMMAND ${RUST_TOOLCHAIN}/cargo rustc --release --target ${RUST_TARGET} -- + -C linker="${LLVM_TOOLCHAIN}/${ANDROID_TARGET}${CMAKE_ANDROID_API}-clang" + DEPENDS setup_rust_target + BYPRODUCTS ${FL_NATIVE_RUST_PATH} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +add_dependencies(fl_native fl_native_rust_build) + +# TODO: check if there's a better way than allowing multiple symbol definitions +target_link_libraries(fl_native + android log -Wl,--whole-archive -Wl,--allow-multiple-definition ${FL_NATIVE_RUST_PATH} +) diff --git a/lib/native/src/main/rust/Cargo.toml b/lib/native/src/main/rust/Cargo.toml new file mode 100644 index 00000000..dee5c903 --- /dev/null +++ b/lib/native/src/main/rust/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "fl_native_rust" +version = "0.1.0" +edition = "2021" + +[lib] +name = "fl_native_rust" +crate-type = ["staticlib"] + +[dependencies] +dummy = { path = "../../../../../libnative/dummy" } +jni = "0.21.1" diff --git a/lib/native/src/main/rust/src/lib.c b/lib/native/src/main/rust/src/lib.c new file mode 100644 index 00000000..38819711 --- /dev/null +++ b/lib/native/src/main/rust/src/lib.c @@ -0,0 +1 @@ +// DO NOT DELETE ELSE CMAKE FAILS diff --git a/lib/native/src/main/rust/src/lib.rs b/lib/native/src/main/rust/src/lib.rs new file mode 100644 index 00000000..68096f57 --- /dev/null +++ b/lib/native/src/main/rust/src/lib.rs @@ -0,0 +1,15 @@ +use dummy; + +use jni::objects::JClass; +use jni::sys::jint; +use jni::JNIEnv; + +#[no_mangle] +pub extern "system" fn Java_org_florisboard_libnative_TestKt_dummyAdd( + _env: JNIEnv, + _class: JClass, + a: jint, + b: jint, +) -> jint { + dummy::addnumbers(a, b) +} diff --git a/libnative/dummy/Cargo.toml b/libnative/dummy/Cargo.toml new file mode 100644 index 00000000..0a4e03f8 --- /dev/null +++ b/libnative/dummy/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "dummy" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/libnative/dummy/src/lib.rs b/libnative/dummy/src/lib.rs new file mode 100644 index 00000000..768315f5 --- /dev/null +++ b/libnative/dummy/src/lib.rs @@ -0,0 +1,3 @@ +pub fn addnumbers(left: i32, right: i32) -> i32 { + left + right +} diff --git a/settings.gradle.kts b/settings.gradle.kts index ac13bb2e..1191d00c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -36,3 +36,4 @@ dependencyResolutionManagement { include(":app") include(":benchmark") include(":lib:kotlin") +include(":lib:native")