diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 00000000..59b35f36 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,91 @@ +name: Android Build + +on: + workflow_dispatch: + pull_request: + paths: + # Android project itself, plus the shared C++ runtime it compiles from source. + - "runtime/android/**" + - "runtime/bindings/android/**" + - "runtime/CMakeLists.txt" + - "runtime/CMakePresets.json" + - "runtime/processor/**" + - "runtime/utils/**" + - "runtime/cmake/**" + - ".github/workflows/android.yml" + +jobs: + build-android: + name: build (android) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + + # Caches downloaded Gradle distribution and dependencies across runs. + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + build-root-directory: runtime/android + + # Puts sdkmanager on PATH, accepts licenses, installs platform + build-tools. + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + with: + packages: 'platforms;android-34 build-tools;34.0.0' + + # sdkmanager has no bare "ndk" package — must install "ndk;X.Y.Z". Pick the + # newest side-by-side NDK from the package list. + - name: Install NDK (latest) + run: | + NDK_PKG=$(sdkmanager --list 2>/dev/null \ + | grep -oE 'ndk;[0-9]+\.[0-9]+\.[0-9]+' \ + | sort -t';' -k2 -V \ + | tail -1) + if [ -z "$NDK_PKG" ]; then + echo "ERROR: no ndk;* package found in sdkmanager --list" >&2 + sdkmanager --list 2>/dev/null | grep -i ndk | head -20 >&2 || true + exit 1 + fi + echo "Installing $NDK_PKG" + sdkmanager --install "$NDK_PKG" + NDK_VER="${NDK_PKG#ndk;}" + echo "Using NDK $NDK_VER" + echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/$NDK_VER" >> "$GITHUB_ENV" + + # Build the native lib with CMake presets and install it into jniLibs//. + # Gradle does NOT drive CMake; it only packages the prebuilt .so. + - name: Build native lib + working-directory: runtime + run: | + cmake --preset android-arm64-v8a + cmake --build --preset android-arm64-v8a + cmake --install build/aarch64-linux-android --component jni + + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + + # Generate the four FST models straight into assets/ so the APK ships with + # them (the app loads zh_tn_* and zh_itn_* at runtime). + - name: Generate model assets + run: | + pip install pynini importlib_resources + assets=runtime/android/app/src/main/assets + python -m tn --language zh --overwrite_cache --cache_dir "$assets" + python -m itn --language zh --overwrite_cache --cache_dir "$assets" + + - name: Build APK + working-directory: runtime/android + run: ./gradlew :app:assembleDebug -PabiFilters=arm64-v8a --no-daemon + + - name: Upload APK + uses: actions/upload-artifact@v4 + with: + name: app-debug-arm64-v8a + path: runtime/android/app/build/outputs/apk/debug/app-debug.apk + if-no-files-found: error diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 2edf961a..866bea35 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -5,6 +5,10 @@ on: pull_request: paths: - "runtime/**" + # Android has its own workflow; don't rebuild the desktop runtime for + # Android-only changes (shared processor/utils/cmake still match above). + - "!runtime/android/**" + - "!runtime/bindings/android/**" - ".github/workflows/runtime.yml" jobs: diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 90396cfd..bcc94962 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -9,8 +9,6 @@ option(BUILD_TESTING "whether to build unit test" OFF) include(FetchContent) set(FETCHCONTENT_QUIET OFF) -get_filename_component(fc_base "fc_base-${CMAKE_CXX_COMPILER_ID}" REALPATH BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(FETCHCONTENT_BASE_DIR ${fc_base}) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) set(CMAKE_POSITION_INDEPENDENT_CODE ON) @@ -25,14 +23,27 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") set(CMAKE_MACOSX_RPATH 1) endif() +if(ANDROID) + set(BUILD_SHARED_LIBS OFF) +endif() + include(openfst) +include(glog) +include(gflags) include_directories(${PROJECT_SOURCE_DIR}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") add_subdirectory(utils) add_subdirectory(processor) -add_subdirectory(bin) + +if(ANDROID) + # JNI shared library; only meaningful when cross-compiling with the NDK. + add_subdirectory(bindings/android) +else() + # Command-line tools are host-only; skip them for Android. + add_subdirectory(bin) +endif() if(BUILD_TESTING) include(gtest) diff --git a/runtime/CMakePresets.json b/runtime/CMakePresets.json new file mode 100644 index 00000000..90619bd9 --- /dev/null +++ b/runtime/CMakePresets.json @@ -0,0 +1,69 @@ +{ + "version": 10, + "cmakeMinimumRequired": { + "major": 3, + "minor": 23, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "binaryDir": "${sourceDir}/build/default.release", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "android-base", + "hidden": true, + "generator": "Ninja", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "$env{ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake", + "ANDROID_PLATFORM": "android-21", + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "android-armeabi-v7a", + "inherits": "android-base", + "binaryDir": "${sourceDir}/build/arm-linux-androideabi", + "cacheVariables": { + "ANDROID_ABI": "armeabi-v7a", + "CMAKE_INSTALL_PREFIX": "${sourceDir}/android/app/src/main/jniLibs/armeabi-v7a" + } + }, + { + "name": "android-arm64-v8a", + "inherits": "android-base", + "binaryDir": "${sourceDir}/build/aarch64-linux-android", + "cacheVariables": { + "ANDROID_ABI": "arm64-v8a", + "CMAKE_INSTALL_PREFIX": "${sourceDir}/android/app/src/main/jniLibs/arm64-v8a" + } + }, + { + "name": "android-x86", + "inherits": "android-base", + "binaryDir": "${sourceDir}/build/i686-linux-android", + "cacheVariables": { + "ANDROID_ABI": "x86", + "CMAKE_INSTALL_PREFIX": "${sourceDir}/android/app/src/main/jniLibs/x86" + } + }, + { + "name": "android-x86_64", + "inherits": "android-base", + "binaryDir": "${sourceDir}/build/x86_64-linux-android", + "cacheVariables": { + "ANDROID_ABI": "x86_64", + "CMAKE_INSTALL_PREFIX": "${sourceDir}/android/app/src/main/jniLibs/x86_64" + } + } + ], + "buildPresets": [ + { "name": "android-armeabi-v7a", "configurePreset": "android-armeabi-v7a" }, + { "name": "android-arm64-v8a", "configurePreset": "android-arm64-v8a" }, + { "name": "android-x86", "configurePreset": "android-x86" }, + { "name": "android-x86_64", "configurePreset": "android-x86_64" } + ] +} diff --git a/runtime/android/.gitignore b/runtime/android/.gitignore index aa724b77..31d4fb7f 100644 --- a/runtime/android/.gitignore +++ b/runtime/android/.gitignore @@ -10,6 +10,5 @@ .DS_Store /build /captures -.externalNativeBuild -.cxx local.properties +app/src/main/jniLibs/ diff --git a/runtime/android/app/build.gradle b/runtime/android/app/build.gradle index 320bbb2c..f42b01a3 100644 --- a/runtime/android/app/build.gradle +++ b/runtime/android/app/build.gradle @@ -1,17 +1,16 @@ plugins { - id 'com.android.application' + alias(libs.plugins.android.application) } -repositories { - jcenter() - maven { - url "https://oss.sonatype.org/content/repositories/snapshots" - } +def nativeAbis() { + return (project.findProperty('abiFilters') ?: 'armeabi-v7a,arm64-v8a,x86,x86_64') + .split(',').collect { it.trim() }.findAll { it } } android { - lintOptions { - abortOnError false + namespace = "com.wenet.WeTextProcessing" + lint { + abortOnError = false } signingConfigs { release { @@ -21,85 +20,49 @@ android { keyPassword '123456' } } - packagingOptions { - jniLibs { - pickFirsts += ['lib/arm64-v8a/libc++_shared.so'] - } - } - configurations { - extractForNativeBuild - } - compileSdkVersion 30 - buildToolsVersion "30.0.3" + compileSdk = 34 defaultConfig { - applicationId "com.mobvoi.WeTextProcessing" - minSdkVersion 21 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" + applicationId = "com.wenet.WeTextProcessing" + minSdk = 21 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - externalNativeBuild { - cmake { - targets "wetextprocessing" - cppFlags "-std=c++14", "-DC10_USE_GLOG", "-DC10_USE_MINIMAL_GLOG", "-DANDROID", "-Wno-c++11-narrowing", "-fexceptions" - } - } + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - ndkVersion '21.1.6352462' ndk { - abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + abiFilters.addAll(nativeAbis()) } } buildTypes { release { - minifyEnabled false - signingConfig signingConfigs.release + minifyEnabled = false + signingConfig = signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } - externalNativeBuild { - cmake { - path "src/main/cpp/CMakeLists.txt" - } - } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } } dependencies { - - implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'com.google.android.material:material:1.2.1' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' - testImplementation 'junit:junit:4.+' - androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' - - implementation 'com.github.pengzhendong:wenet-openfst-android:1.0.2' - extractForNativeBuild 'com.github.pengzhendong:wenet-openfst-android:1.0.2' + implementation libs.androidx.appcompat + implementation libs.material + implementation libs.androidx.constraintlayout + testImplementation libs.junit + androidTestImplementation libs.androidx.junit + androidTestImplementation libs.androidx.espresso.core } -task extractAARForNativeBuild { - doLast { - configurations.extractForNativeBuild.files.each { - def file = it.absoluteFile - copy { - from zipTree(file) - into "$buildDir/$file.name" - include "headers/**" - include "jni/**" - } - } - } -} - -tasks.whenTaskAdded { task -> - if (task.name.contains('externalNativeBuild')) { - task.dependsOn(extractAARForNativeBuild) - } -} +// Native libs are NOT built by Gradle. Build them beforehand with CMake presets +// (from repo root) so the .so files exist in app/src/main/jniLibs//: +// export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/ +// cd runtime +// cmake --preset android-arm64-v8a +// cmake --build --preset android-arm64-v8a +// cmake --install build/aarch64-linux-android --component jni +// Gradle only packages whatever is already present under jniLibs. diff --git a/runtime/android/app/src/androidTest/java/com/mobvoi/WeTextProcessing/ExampleInstrumentedTest.java b/runtime/android/app/src/androidTest/java/com/wenet/WeTextProcessing/ExampleInstrumentedTest.java similarity index 84% rename from runtime/android/app/src/androidTest/java/com/mobvoi/WeTextProcessing/ExampleInstrumentedTest.java rename to runtime/android/app/src/androidTest/java/com/wenet/WeTextProcessing/ExampleInstrumentedTest.java index b1d0d3f4..480682ca 100644 --- a/runtime/android/app/src/androidTest/java/com/mobvoi/WeTextProcessing/ExampleInstrumentedTest.java +++ b/runtime/android/app/src/androidTest/java/com/wenet/WeTextProcessing/ExampleInstrumentedTest.java @@ -1,4 +1,4 @@ -package com.mobvoi.WeTextProcessing; +package com.wenet.WeTextProcessing; import android.content.Context; @@ -21,6 +21,6 @@ public class ExampleInstrumentedTest { public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - assertEquals("com.mobvoi.WeTextProcessing", appContext.getPackageName()); + assertEquals("com.wenet.WeTextProcessing", appContext.getPackageName()); } -} \ No newline at end of file +} diff --git a/runtime/android/app/src/main/AndroidManifest.xml b/runtime/android/app/src/main/AndroidManifest.xml index fd2ed433..b3979eeb 100644 --- a/runtime/android/app/src/main/AndroidManifest.xml +++ b/runtime/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> - + diff --git a/runtime/android/app/src/main/assets/README.md b/runtime/android/app/src/main/assets/README.md index d8870ca9..91a046ce 100644 --- a/runtime/android/app/src/main/assets/README.md +++ b/runtime/android/app/src/main/assets/README.md @@ -1 +1,29 @@ -put tagger.fst and verbalizer.fst here. +# Android model assets + +The app loads four FST models at runtime (see `MainActivity.java` and +`wetextprocessing.cc`). They are **not** checked into git and must be placed in +this directory before building the APK: + +- `zh_tn_tagger.fst` +- `zh_tn_verbalizer.fst` +- `zh_itn_tagger.fst` +- `zh_itn_verbalizer.fst` + +## How to generate + +From the repository root, generate the TN (text normalization) and ITN +(inverse text normalization) models straight into this folder: + +```bash +assets=runtime/android/app/src/main/assets + +# TN: produces zh_tn_tagger.fst and zh_tn_verbalizer.fst +python -m tn --language zh --overwrite_cache --cache_dir "$assets" + +# ITN: produces zh_itn_tagger.fst and zh_itn_verbalizer.fst +python -m itn --language zh --overwrite_cache --cache_dir "$assets" +``` + +`pynini` is required to build the models (`pip install pynini importlib_resources`). +On startup `MainActivity.assetsInit()` unpacks these files from assets into the +app's `filesDir`, where the native `Processor` reads them. diff --git a/runtime/android/app/src/main/cpp/CMakeLists.txt b/runtime/android/app/src/main/cpp/CMakeLists.txt deleted file mode 100644 index 93de521a..00000000 --- a/runtime/android/app/src/main/cpp/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -cmake_minimum_required(VERSION 3.4.1) -set(TARGET wetextprocessing) -project(${TARGET} CXX) -set(CMAKE_CXX_STANDARD 14) -include(ExternalProject) - -set(CMAKE_VERBOSE_MAKEFILE on) -set(build_DIR ${CMAKE_SOURCE_DIR}/../../../build) -list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -string(REPLACE "-Wl,--exclude-libs,libgcc_real.a" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") - -include(openfst) - -include_directories( - ${CMAKE_SOURCE_DIR} -) - -add_subdirectory(utils) -add_subdirectory(processor) - -link_libraries(wetext_processor android) -add_library(${TARGET} SHARED wetextprocessing.cc) diff --git a/runtime/android/app/src/main/cpp/cmake b/runtime/android/app/src/main/cpp/cmake deleted file mode 120000 index fdde3f06..00000000 --- a/runtime/android/app/src/main/cpp/cmake +++ /dev/null @@ -1 +0,0 @@ -../../../../../cmake \ No newline at end of file diff --git a/runtime/android/app/src/main/cpp/patch b/runtime/android/app/src/main/cpp/patch deleted file mode 120000 index fc351af8..00000000 --- a/runtime/android/app/src/main/cpp/patch +++ /dev/null @@ -1 +0,0 @@ -../../../../../patch \ No newline at end of file diff --git a/runtime/android/app/src/main/cpp/processor b/runtime/android/app/src/main/cpp/processor deleted file mode 120000 index 57b2f8d5..00000000 --- a/runtime/android/app/src/main/cpp/processor +++ /dev/null @@ -1 +0,0 @@ -../../../../../processor \ No newline at end of file diff --git a/runtime/android/app/src/main/cpp/utils b/runtime/android/app/src/main/cpp/utils deleted file mode 120000 index 221c9349..00000000 --- a/runtime/android/app/src/main/cpp/utils +++ /dev/null @@ -1 +0,0 @@ -../../../../../utils \ No newline at end of file diff --git a/runtime/android/app/src/main/java/com/mobvoi/WeTextProcessing/MainActivity.java b/runtime/android/app/src/main/java/com/wenet/WeTextProcessing/MainActivity.java similarity index 98% rename from runtime/android/app/src/main/java/com/mobvoi/WeTextProcessing/MainActivity.java rename to runtime/android/app/src/main/java/com/wenet/WeTextProcessing/MainActivity.java index fac570f1..75923dd3 100644 --- a/runtime/android/app/src/main/java/com/mobvoi/WeTextProcessing/MainActivity.java +++ b/runtime/android/app/src/main/java/com/wenet/WeTextProcessing/MainActivity.java @@ -1,4 +1,4 @@ -package com.mobvoi.WeTextProcessing; +package com.wenet.WeTextProcessing; import android.Manifest; import android.content.Context; @@ -75,4 +75,4 @@ protected void onCreate(Bundle savedInstanceState) { textView.setText(result); }); } -} \ No newline at end of file +} diff --git a/runtime/android/app/src/main/java/com/mobvoi/WeTextProcessing/WeTextProcessing.java b/runtime/android/app/src/main/java/com/wenet/WeTextProcessing/WeTextProcessing.java similarity index 87% rename from runtime/android/app/src/main/java/com/mobvoi/WeTextProcessing/WeTextProcessing.java rename to runtime/android/app/src/main/java/com/wenet/WeTextProcessing/WeTextProcessing.java index f1332911..70f0555c 100644 --- a/runtime/android/app/src/main/java/com/mobvoi/WeTextProcessing/WeTextProcessing.java +++ b/runtime/android/app/src/main/java/com/wenet/WeTextProcessing/WeTextProcessing.java @@ -1,4 +1,4 @@ -package com.mobvoi.WeTextProcessing; +package com.wenet.WeTextProcessing; public class WeTextProcessing { diff --git a/runtime/android/app/src/main/jniLibs/.gitignore b/runtime/android/app/src/main/jniLibs/.gitignore new file mode 100644 index 00000000..429801dd --- /dev/null +++ b/runtime/android/app/src/main/jniLibs/.gitignore @@ -0,0 +1,2 @@ +# Prebuilt native libs (cmake --install --component jni); not committed. +* diff --git a/runtime/android/app/src/test/java/com/mobvoi/WeTextProcessing/ExampleUnitTest.java b/runtime/android/app/src/test/java/com/wenet/WeTextProcessing/ExampleUnitTest.java similarity index 90% rename from runtime/android/app/src/test/java/com/mobvoi/WeTextProcessing/ExampleUnitTest.java rename to runtime/android/app/src/test/java/com/wenet/WeTextProcessing/ExampleUnitTest.java index a34519e2..31453c65 100644 --- a/runtime/android/app/src/test/java/com/mobvoi/WeTextProcessing/ExampleUnitTest.java +++ b/runtime/android/app/src/test/java/com/wenet/WeTextProcessing/ExampleUnitTest.java @@ -1,4 +1,4 @@ -package com.mobvoi.WeTextProcessing; +package com.wenet.WeTextProcessing; import org.junit.Test; @@ -14,4 +14,4 @@ public class ExampleUnitTest { public void addition_isCorrect() { assertEquals(4, 2 + 2); } -} \ No newline at end of file +} diff --git a/runtime/android/build.gradle b/runtime/android/build.gradle index e2a37d0a..ce201a57 100644 --- a/runtime/android/build.gradle +++ b/runtime/android/build.gradle @@ -1,21 +1,3 @@ -buildscript { - repositories { - google() - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:4.2.2' - } +plugins { + alias(libs.plugins.android.application) apply false } - -allprojects { - repositories { - google() - jcenter() - maven { url 'https://jitpack.io' } - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} \ No newline at end of file diff --git a/runtime/android/gradle.properties b/runtime/android/gradle.properties index 52f5917c..6826e61b 100644 --- a/runtime/android/gradle.properties +++ b/runtime/android/gradle.properties @@ -14,6 +14,4 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # AndroidX package structure to make it clearer which packages are bundled with the # Android operating system, and which are packaged with your app"s APK # https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true -# Automatically convert third-party libraries to use AndroidX -android.enableJetifier=true \ No newline at end of file +android.useAndroidX=true \ No newline at end of file diff --git a/runtime/android/gradle/gradle-daemon-jvm.properties b/runtime/android/gradle/gradle-daemon-jvm.properties new file mode 100644 index 00000000..6c1139ec --- /dev/null +++ b/runtime/android/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,12 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/73bcfb608d1fde9fb62e462f834a3299/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/846ee0d876d26a26f37aa1ce8de73224/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/9482ddec596298c84656d31d16652665/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/39701d92e1756bb2f141eb67cd4c660e/redirect +toolchainVersion=21 diff --git a/runtime/android/gradle/libs.versions.toml b/runtime/android/gradle/libs.versions.toml new file mode 100644 index 00000000..07d1865d --- /dev/null +++ b/runtime/android/gradle/libs.versions.toml @@ -0,0 +1,19 @@ +[versions] +agp = "9.2.1" +appcompat = "1.7.0" +material = "1.12.0" +constraintlayout = "2.1.4" +junit = "4.13.2" +androidxJunit = "1.2.1" +espresso = "3.6.1" + +[libraries] +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxJunit" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } diff --git a/runtime/android/gradle/wrapper/gradle-wrapper.properties b/runtime/android/gradle/wrapper/gradle-wrapper.properties index ffed3a25..221c4f98 100644 --- a/runtime/android/gradle/wrapper/gradle-wrapper.properties +++ b/runtime/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/runtime/android/settings.gradle b/runtime/android/settings.gradle index fd81acc7..9827b994 100644 --- a/runtime/android/settings.gradle +++ b/runtime/android/settings.gradle @@ -1,2 +1,26 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex "com\\.android.*" + includeGroupByRegex "com\\.google.*" + includeGroupByRegex "androidx.*" + } + } + mavenCentral() + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS + repositories { + google() + mavenCentral() + maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") } + maven { url = uri("https://jitpack.io") } + } +} + +rootProject.name = "WeTextProcessing" include ':app' -rootProject.name = "WeTextProcessing" \ No newline at end of file diff --git a/runtime/bindings/android/CMakeLists.txt b/runtime/bindings/android/CMakeLists.txt new file mode 100644 index 00000000..4de488a5 --- /dev/null +++ b/runtime/bindings/android/CMakeLists.txt @@ -0,0 +1,17 @@ +# Android JNI binding. Included by the top-level runtime/CMakeLists.txt only when +# configuring with the NDK toolchain (if(ANDROID)). It reuses the parent's +# openfst/glog/gflags and the wetext_processor/wetext_utils targets, so it just +# defines the JNI shared library and how to install it. +add_library(wetextprocessing SHARED ${CMAKE_CURRENT_SOURCE_DIR}/wetextprocessing.cc) + +target_link_libraries(wetextprocessing PRIVATE wetext_processor) + +# Install ONLY this target under its own component, so +# cmake --install --component jni +# drops the single libwetextprocessing.so straight into CMAKE_INSTALL_PREFIX +# (set per-ABI to .../jniLibs/ by the presets). Using a dedicated component +# avoids pulling in the install rules that glog/gflags register via FetchContent, +# which would otherwise litter jniLibs with headers and static libs. +install(TARGETS wetextprocessing + LIBRARY DESTINATION "." COMPONENT jni + RUNTIME DESTINATION "." COMPONENT jni) diff --git a/runtime/android/app/src/main/cpp/wetextprocessing.cc b/runtime/bindings/android/wetextprocessing.cc similarity index 97% rename from runtime/android/app/src/main/cpp/wetextprocessing.cc rename to runtime/bindings/android/wetextprocessing.cc index 4c3fbc5c..3544cfc8 100644 --- a/runtime/android/app/src/main/cpp/wetextprocessing.cc +++ b/runtime/bindings/android/wetextprocessing.cc @@ -57,7 +57,7 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) { return JNI_ERR; } - jclass c = env->FindClass("com/mobvoi/WeTextProcessing/WeTextProcessing"); + jclass c = env->FindClass("com/wenet/WeTextProcessing/WeTextProcessing"); if (c == nullptr) { return JNI_ERR; } diff --git a/runtime/cmake/glog.cmake b/runtime/cmake/glog.cmake index c04225cb..b5e011e0 100644 --- a/runtime/cmake/glog.cmake +++ b/runtime/cmake/glog.cmake @@ -1,3 +1,12 @@ +if(ANDROID) + # NDK r30+ ships a host libunwind.so under toolchains/.../prebuilt/linux-x86_64/lib/ + # that glog's find_package(Unwind) picks up and unconditionally links (it gates + # linking on Unwind_FOUND alone). That x86_64 host library is incompatible with the + # aarch64 target. WITH_UNWIND=none sets CMAKE_DISABLE_FIND_PACKAGE_Unwind so the + # lookup is skipped entirely; Clang already provides the unwinder for exceptions. + set(WITH_UNWIND none CACHE STRING "" FORCE) +endif() + FetchContent_Declare(glog URL https://github.com/google/glog/archive/refs/tags/v0.7.1.zip URL_HASH SHA256=c17d85c03ad9630006ef32c7be7c65656aba2e7e2fbfc82226b7e680c771fc88 diff --git a/runtime/cmake/openfst.cmake b/runtime/cmake/openfst.cmake index 485325c9..5b748f7c 100644 --- a/runtime/cmake/openfst.cmake +++ b/runtime/cmake/openfst.cmake @@ -1,40 +1,27 @@ -if(NOT ANDROID) - include(gflags) - # We can't build glog with gflags, unless gflags is pre-installed. - # If build glog with pre-installed gflags, there will be conflict. - set(WITH_GFLAGS OFF CACHE BOOL "whether build glog with gflags" FORCE) - include(glog) +set(HAVE_BIN OFF CACHE BOOL "Build the fst binaries" FORCE) +set(HAVE_SCRIPT OFF CACHE BOOL "Build the fstscript" FORCE) +set(HAVE_COMPACT OFF CACHE BOOL "Build compact" FORCE) +set(HAVE_CONST OFF CACHE BOOL "Build const" FORCE) +set(HAVE_GRM OFF CACHE BOOL "Build grm" FORCE) +set(HAVE_FAR OFF CACHE BOOL "Build far" FORCE) +set(HAVE_PDT OFF CACHE BOOL "Build pdt" FORCE) +set(HAVE_MPDT OFF CACHE BOOL "Build mpdt" FORCE) +set(HAVE_LINEAR OFF CACHE BOOL "Build linear" FORCE) +set(HAVE_LOOKAHEAD OFF CACHE BOOL "Build lookahead" FORCE) +set(HAVE_NGRAM OFF CACHE BOOL "Build ngram" FORCE) +set(HAVE_SPECIAL OFF CACHE BOOL "Build special" FORCE) - set(HAVE_BIN OFF CACHE BOOL "Build the fst binaries" FORCE) - set(HAVE_SCRIPT OFF CACHE BOOL "Build the fstscript" FORCE) - set(HAVE_COMPACT OFF CACHE BOOL "Build compact" FORCE) - set(HAVE_CONST OFF CACHE BOOL "Build const" FORCE) - set(HAVE_GRM OFF CACHE BOOL "Build grm" FORCE) - set(HAVE_FAR OFF CACHE BOOL "Build far" FORCE) - set(HAVE_PDT OFF CACHE BOOL "Build pdt" FORCE) - set(HAVE_MPDT OFF CACHE BOOL "Build mpdt" FORCE) - set(HAVE_LINEAR OFF CACHE BOOL "Build linear" FORCE) - set(HAVE_LOOKAHEAD OFF CACHE BOOL "Build lookahead" FORCE) - set(HAVE_NGRAM OFF CACHE BOOL "Build ngram" FORCE) - set(HAVE_SPECIAL OFF CACHE BOOL "Build special" FORCE) - - if(MSVC) - add_compile_options(/W0 /wd4244 /wd4267) - endif() - - # "OpenFST port for Windows" builds openfst with cmake for multiple platforms. - # Openfst is compiled with glog/gflags to avoid log and flag conflicts with log and flags in wenet/libtorch. - # To build openfst with gflags and glog, we comment out some vars of {flags, log}.h and flags.cc. - set(openfst_SOURCE_DIR ${fc_base}/openfst-src CACHE PATH "OpenFST source directory") - FetchContent_Declare(openfst - URL https://github.com/csukuangfj/openfst/archive/refs/tags/v1.8.5-2026-06-15.tar.gz - URL_HASH SHA256=5f9323ded5c9cf4d4e23325dd92652b18b553556ad92b59996e687ebd9688490 - ) - FetchContent_MakeAvailable(openfst) - include_directories(${openfst_SOURCE_DIR}/src/include) -else() - set(openfst_BINARY_DIR ${build_DIR}/wenet-openfst-android-1.0.2.aar/jni) - include_directories(${openfst_BINARY_DIR}/include) - link_directories(${openfst_BINARY_DIR}/${ANDROID_ABI}) - link_libraries(log glog fst gflags_nothreads) +if(MSVC) + add_compile_options(/W0 /wd4244 /wd4267) endif() + +# "OpenFST port for Windows" builds openfst with cmake for multiple platforms. +# Openfst is compiled with glog/gflags to avoid log and flag conflicts with log and flags in wenet/libtorch. +# To build openfst with gflags and glog, we comment out some vars of {flags, log}.h and flags.cc. +set(openfst_SOURCE_DIR ${fc_base}/openfst-src CACHE PATH "OpenFST source directory") + FetchContent_Declare(openfst + URL https://github.com/csukuangfj/openfst/archive/refs/tags/v1.8.5-2026-06-15.tar.gz + URL_HASH SHA256=5f9323ded5c9cf4d4e23325dd92652b18b553556ad92b59996e687ebd9688490 +) +FetchContent_MakeAvailable(openfst) +include_directories(${openfst_SOURCE_DIR}/src/include)