diff --git a/.github/workflows/build-quick.yml b/.github/workflows/build-quick.yml index 9846a93ee..61921878f 100644 --- a/.github/workflows/build-quick.yml +++ b/.github/workflows/build-quick.yml @@ -261,7 +261,7 @@ jobs: run: ./check-rust.bat - name: Run tests (Unit) - run: ./gradlew test rsdroid:lint rsdroid-android:lint ktlintCheck --daemon + run: ./gradlew test rsdroid-android:lint ktlintCheck --daemon - name: Run tests (Emulator) uses: reactivecircus/android-emulator-runner@v2 @@ -279,13 +279,13 @@ jobs: sleep 5 ./gradlew rsdroid-instrumented:connectedCheck - - name: Upload rsdroid AAR as artifact + - name: Upload backend core JARs as artifact uses: actions/upload-artifact@v6 if: '!cancelled()' with: - name: rsdroid-aar-${{ matrix.os }} + name: rsdroid-core-${{ matrix.os }} if-no-files-found: error - path: rsdroid/build/outputs/aar + path: rsdroid/build/libs - name: Upload Android wrapper AAR as artifact uses: actions/upload-artifact@v6 diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index a794e8f42..bf3719588 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -168,12 +168,12 @@ jobs: retry_on: error command: ./build.sh - - name: Upload rsdroid AAR as artifact + - name: Upload backend core JARs as artifact uses: actions/upload-artifact@v6 with: - name: rsdroid-aar + name: rsdroid-core if-no-files-found: error - path: rsdroid/build/outputs/aar + path: rsdroid/build/libs - name: Upload Android wrapper AAR as artifact uses: actions/upload-artifact@v6 @@ -191,7 +191,7 @@ jobs: # following steps only run on workflow dispatch - - name: Publish backend and Android wrapper AARs to Maven + - name: Publish backend core JAR and Android wrapper AAR to Maven if: "${{ github.event.inputs.mavenPublish != '' && github.event_name == 'workflow_dispatch'}}" env: ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_PRIVATE_KEY }} diff --git a/README.md b/README.md index ff343d43b..c6ae309cc 100644 --- a/README.md +++ b/README.md @@ -92,8 +92,8 @@ set PATH=%PATH%;c:\msys64\usr\bin The build produces three artifacts: -- The backend API .aar in `rsdroid/build/outputs/aar`, which contains the backend -Kotlin code and generated Protocol Buffer classes. +- The backend API .jar in `rsdroid/build/libs`, published as `anki-backend-core`, +which contains the backend Kotlin code and generated Protocol Buffer classes. - The Android .aar in `rsdroid-android/build/outputs/aar`, which contains the SQLite adapter, web assets, and Anki backend code compiled for Android. Its published Maven metadata declares an API dependency on the backend API artifact. @@ -158,13 +158,11 @@ local_backend=true ``` The local dependency configuration in AnkiDroid must include both -`rsdroid/build/outputs/aar/rsdroid-release.aar` and +`rsdroid/build/libs/rsdroid.jar` and `rsdroid-android/build/outputs/aar/rsdroid-android-release.aar`. -Check `Anki-AndroidBackend/gradle.properties`'s `BACKEND_VERSION` and -`Anki-Android/build.gradle`'s `ext.ankidroid_backend_version`. Both variables -should have the same value. If it is not the case, you must edit Anki-Android's -one. +Ensure `VERSION_NAME` in this repository's `gradle.properties` matches +`ankiBackend` in AnkiDroid's `gradle/libs.versions.toml`. After making the change, you should be able to build and run the project on an x86_64 emulator/device (arm64 on M1 Macs), and run unit tests. @@ -216,7 +214,7 @@ the repository. 1. Head over to the main `Anki-Android` repository and update the `AnkiDroid/build.gradle` file there to adopt the new backend version once it shows up in -https://repo1.maven.org/maven2/io/github/david-allison/anki-android-backend/ +https://repo1.maven.org/maven2/io/github/david-allison/anki-backend-android/ ## test library copies diff --git a/build.gradle.kts b/build.gradle.kts index e80ab7d67..36d90a3ca 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,9 @@ plugins { alias(libs.plugins.ktlint.gradle.plugin) apply false } +// One Rust pipeline generates native libraries and API sources for all modules. +apply(from = "build-rust.gradle") + allprojects { repositories { google() diff --git a/build_rust/src/main.rs b/build_rust/src/main.rs index e00e335e5..5bed5b99d 100644 --- a/build_rust/src/main.rs +++ b/build_rust/src/main.rs @@ -47,7 +47,12 @@ fn run_gradle() -> Result<()> { Command::new("./gradlew") }; cmd.env("RUNNING_FROM_BUILD_SCRIPT", "1") - .args(["assembleRelease", "rsdroid-testing:build"]) + // assembleRelease covers Android modules; also build the JVM core JAR. + .args([ + "assembleRelease", + "rsdroid:assemble", + "rsdroid-testing:build", + ]) .ensure_success()?; } Ok(()) diff --git a/check-droid.bat b/check-droid.bat index de3cfdd50..c18ac80a4 100755 --- a/check-droid.bat +++ b/check-droid.bat @@ -3,4 +3,4 @@ if not exist rsdroid-android\build\outputs\aar\rsdroid-android-release.aar ( exit 1 ) -./gradlew rsdroid:lint rsdroid-android:lint rsdroid-instrumented:connectedCheck +./gradlew rsdroid-android:lint rsdroid-instrumented:connectedCheck diff --git a/check-droid.sh b/check-droid.sh index 9d4f2034d..d6168bea2 100755 --- a/check-droid.sh +++ b/check-droid.sh @@ -9,4 +9,4 @@ test -f rsdroid-android/build/outputs/aar/rsdroid-android-release.aar || ( . ./set-android-ndk-home.sh -./gradlew rsdroid:lint rsdroid-android:lint rsdroid-instrumented:connectedCheck +./gradlew rsdroid-android:lint rsdroid-instrumented:connectedCheck diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 7b1cde754..44af9760f 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -8,11 +8,11 @@ This repo is comprised of three main components: of only three different functions: `openBackend()`, `closeBackend()`, and `runMethodRaw()`. When one of these functions is called by Java code, `rslib-bridge` takes care of converting the Java objects to a native Rust representation, and then passes the call on to Anki's Rust backend, which gets included in `rslib-bridge` via the `anki[/rslib]` submodule. -- `rsdroid` is a Kotlin library that provides a friendly interface to the backend code. The bulk of its code is automatically - generated from the service definitions in `anki/proto/anki`. It also exposes the Rust database functionality - through `SQLHandler`. +- `rsdroid` is a Kotlin/JVM library, published as `anki-backend-core`, that provides a friendly interface to the backend code. + Its JAR targets Java 11. The bulk of its code is automatically generated from the service definitions in `anki/proto/anki`. + It also exposes the Rust database functionality through `SQLHandler`. - `rsdroid-android` is the Android entry point, published as `anki-backend-android`. It is - referenced by `Anki-Android`, packages the Android native libraries (`librsdroid.so` for each + referenced by `Anki-Android`, packages the Android native libraries (`librsdroid.so` for each supported architecture), and bundles web assets as Android assets. It exposes `rsdroid` through an `api` dependency. Other folders: @@ -21,7 +21,7 @@ Other folders: `/tools/` Scripts to generate the backend interface and translations -`rsdroid-testing` - Builds a testing library targetting desktop machines, for use with Robolectric +`rsdroid-testing` - Builds native libraries for desktop machines, for use in JVM tests and Robolectric `rsdroid-instrumented` - Android Instrumented Test diff --git a/gradle.properties b/gradle.properties index a1a841c08..8c8131a45 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,7 +19,7 @@ android.useAndroidX=true android.enableJetifier=false GROUP=io.github.david-allison -VERSION_NAME=0.1.75-anki26.09.3 +VERSION_NAME=0.1.76-anki26.09.3 POM_INCEPTION_YEAR=2020 diff --git a/rsdroid-android/README.md b/rsdroid-android/README.md index 307541cce..791235fab 100644 --- a/rsdroid-android/README.md +++ b/rsdroid-android/README.md @@ -4,6 +4,9 @@ Android entry point for the Anki backend. This Android library exposes [`:rsdroid`](../rsdroid) through an `api` dependency, so consumers can use the backend API through this module. + +Maven consumers should depend on ths via `io.github.david-allison:anki-backend-android`. + ## Contents `anki-backend-android` contains: diff --git a/rsdroid-android/build.gradle b/rsdroid-android/build.gradle index bc0d35e9a..dfa4f9022 100644 --- a/rsdroid-android/build.gradle +++ b/rsdroid-android/build.gradle @@ -9,7 +9,7 @@ plugins { } // The Rust build generates the core sources and this module's native libraries and assets. -tasks.named('preBuild').configure { dependsOn(':rsdroid:buildRust') } +tasks.named('preBuild').configure { dependsOn(':buildRust') } android { namespace = 'net.ankiweb.rsdroid.android' diff --git a/rsdroid-testing/build.gradle b/rsdroid-testing/build.gradle index b2ba7fda0..ad4be2256 100644 --- a/rsdroid-testing/build.gradle +++ b/rsdroid-testing/build.gradle @@ -25,8 +25,6 @@ plugins { alias(libs.plugins.gradle.maven.publish.plugin) } -apply from: "$rootDir/build-rust.gradle" - dependencies { implementation libs.kotlin.stdlib.jdk8 testImplementation libs.junit.jupiter @@ -39,8 +37,10 @@ test { systemProperty 'rsdroidTesting.testClasspath', sourceSets.test.runtimeClasspath.asPath } +// The native libraries must exist before they are copied into the test support JAR. +tasks.named('processResources').configure { dependsOn(':buildRust') } + jar { - mustRunAfter("buildRust") sourceSets { main { resources { diff --git a/rsdroid/build.gradle b/rsdroid/build.gradle index d2e30cc7c..2a213d57d 100644 --- a/rsdroid/build.gradle +++ b/rsdroid/build.gradle @@ -1,86 +1,68 @@ +import com.vanniktech.maven.publish.KotlinJvm +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + plugins { - alias(libs.plugins.android.library) // required for aar generation to link to from AnkiDroid + id 'java-library' + alias(libs.plugins.kotlin.jvm) id 'signing' alias(libs.plugins.gradle.maven.publish.plugin) } -apply from: "$rootDir/build-rust.gradle" -preBuild.dependsOn "buildRust" - def gitHead = { File repo -> providers.exec { commandLine "git", "-C", repo.absolutePath, "rev-parse", "HEAD" }.standardOutput.asText.map { it.trim() } } -// Generate BuildConfig -androidComponents { - onVariants(selector().all()) { variant -> - def generateBuildConfig = tasks.register("generate${variant.name.capitalize()}BuildConfig", GenerateBuildConfigTask) { - ankiCommitHash = gitHead(new File(rootDir, "anki")) - backendGitCommitHash = gitHead(rootDir) - ankiDesktopVersion = providers.gradleProperty("VERSION_NAME").map { it.replaceFirst(/.*anki/, "") } - cargoLock = rootProject.file("Cargo.lock") - } - variant.sources.kotlin.addGeneratedSourceDirectory(generateBuildConfig) { it.outputDir } - } +def generateBuildConfig = tasks.register("generateBuildConfig", GenerateBuildConfigTask) { + ankiCommitHash = gitHead(new File(rootDir, "anki")) + backendGitCommitHash = gitHead(rootDir) + ankiDesktopVersion = providers.gradleProperty("VERSION_NAME").map { it.replaceFirst(/.*anki/, "") } + cargoLock = rootProject.file("Cargo.lock") + outputDir = layout.buildDirectory.dir("generated/source/buildConfig") } -android { - namespace = 'net.ankiweb.rsdroid' - compileSdk = libs.versions.compileSdk.get().toInteger() - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } - - defaultConfig { - minSdk = libs.versions.minSdk.get().toInteger() - - consumerProguardFiles "consumer-rules.pro" - } +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} - buildTypes { - release { - minifyEnabled = false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } +tasks.withType(KotlinCompile).configureEach { + compilerOptions { + jvmTarget = JvmTarget.JVM_11 } +} - testOptions { - targetSdk = libs.versions.targetSdk.get().toInteger() +sourceSets { + main { + kotlin.srcDir("build/generated/source/backend") + kotlin.srcDir(generateBuildConfig.flatMap { it.outputDir }) + // protoc emits Java messages alongside the generated Kotlin API. + java.srcDir("build/generated/source/backend") } +} - lint { - targetSdk = libs.versions.targetSdk.get().toInteger() - } +tasks.named('compileKotlin').configure { dependsOn(':buildRust', 'installGitHook') } +tasks.named('compileJava').configure { dependsOn(':buildRust') } +tasks.matching { it.name == 'sourcesJar' }.configureEach { dependsOn(':buildRust') } - sourceSets { - main { - kotlin.srcDirs += "build/generated/source/backend" - java.srcDirs += "build/generated/source/backend" - resources { - srcDirs "src/main/resources" - } - } - } +test { + useJUnit() } dependencies { - // Protobuf is part of the ABI, so include it as a compile/api dependency. + // These types and annotations appear in the public API. api libs.protobuf.kotlin.lite - // JsonArray is returned by the public fullQuery API. api libs.kotlinx.serialization.json + api libs.androidx.annotation - implementation libs.androidx.annotation implementation libs.slf4j.api testImplementation libs.junit4 testImplementation project(path: ':rsdroid-testing') } - // Install Git pre-commit hook for format-rust tasks.register('installGitHook', Copy) { from new File(rootProject.rootDir, 'pre-commit') @@ -92,9 +74,10 @@ tasks.register('installGitHook', Copy) { } } // to run manually: `./gradlew installGitHook` -tasks.named('preBuild').configure { dependsOn('installGitHook') } mavenPublishing { + // Publish sources and an empty Javadoc JAR for the Kotlin/generated API. + configure(new KotlinJvm()) // Use https://central.sonatype.com/account with david-allison's GitHub login, not Google // the host should match rsdroid-testing publishToMavenCentral(true) // "true" means publish+release automatically+unattended diff --git a/rsdroid/consumer-rules.pro b/rsdroid/consumer-rules.pro deleted file mode 100644 index e69de29bb..000000000 diff --git a/rsdroid/gradle.properties b/rsdroid/gradle.properties index 403409e42..f7dca096a 100644 --- a/rsdroid/gradle.properties +++ b/rsdroid/gradle.properties @@ -1,5 +1,5 @@ -POM_ARTIFACT_ID=anki-android-backend -POM_PACKAGING=aar +POM_ARTIFACT_ID=anki-backend-core +POM_PACKAGING=jar -POM_NAME=Anki Android Backend -POM_DESCRIPTION=JNI bridge between AnkiDroid and Anki Desktop\'s Rust code \ No newline at end of file +POM_NAME=Anki Backend (Core) +POM_DESCRIPTION=JVM API for Anki Desktop's Rust backend; Android integration is provided by anki-backend-android diff --git a/rsdroid/proguard-rules.pro b/rsdroid/proguard-rules.pro deleted file mode 100644 index 481bb4348..000000000 --- a/rsdroid/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/tools/install-prerequisites-tartelet.sh b/tools/install-prerequisites-tartelet.sh index f70b3de41..ffb602d33 100755 --- a/tools/install-prerequisites-tartelet.sh +++ b/tools/install-prerequisites-tartelet.sh @@ -46,7 +46,7 @@ cd .. # from build-quick.yml cargo run -p build_rust ./check-rust.sh -./gradlew test rsdroid:lint +./gradlew test rsdroid-android:lint # from build-release.yml export ALL_ARCHS=1