diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e4481b..9a8e820 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,12 +85,85 @@ jobs: flutter pub get flutter build apk --debug + # The example's MainActivity is Kotlin. A build that skipped Kotlin + # compilation still packages an APK, so check the class is in it. + - name: Example APK contains the Kotlin MainActivity + working-directory: example + run: | + apk=build/app/outputs/flutter-apk/app-debug.apk + if ! unzip -p "$apk" 'classes*.dex' | grep -aq 'Lme/didit/sdk/sdk_flutter_example/MainActivity;'; then + echo "::error::$apk has no MainActivity class: the example's Kotlin sources were not compiled" + exit 1 + fi + # The plugin's JVM unit tests (android/src/test) against the released # native AAR, inside the Gradle project the build above generated. - name: Plugin unit tests (Android) working-directory: example/android run: ./gradlew :didit_sdk:testDebugUnitTest + build-android-example-agp9: + name: Example app builds (Android, AGP 9 built-in Kotlin) + runs-on: ubuntu-latest + timeout-minutes: 30 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '17' + + - uses: subosito/flutter-action@v2 + with: + channel: stable + cache: true + + # The example stays on AGP 8 in the repository (the job above); this run + # moves it to AGP 9 with built-in Kotlin, which refuses any project that + # also applies the Kotlin Gradle Plugin (KGP). Setting the flag explicitly + # also stops Flutter from writing android.builtInKotlin=false. + - name: Move the example to AGP 9 with built-in Kotlin + working-directory: example/android + run: | + sed -i 's/id("com.android.application") version "[^"]*"/id("com.android.application") version "9.2.1"/' settings.gradle.kts + sed -i 's/gradle-[0-9.]*-all\.zip/gradle-9.5.1-all.zip/' gradle/wrapper/gradle-wrapper.properties + printf '\nandroid.builtInKotlin=true\nandroid.newDsl=false\n' >> gradle.properties + grep -q 'id("com.android.application") version "9.2.1"' settings.gradle.kts + grep -q 'gradle-9.5.1-all.zip' gradle/wrapper/gradle-wrapper.properties + + - name: Build example APK + working-directory: example + run: | + flutter pub get + flutter build apk --debug 2>&1 | tee "$RUNNER_TEMP/build-apk.log" + + # Built-in Kotlin must compile the example's Kotlin MainActivity too. + - name: Example APK contains the Kotlin MainActivity + working-directory: example + run: | + apk=build/app/outputs/flutter-apk/app-debug.apk + if ! unzip -p "$apk" 'classes*.dex' | grep -aq 'Lme/didit/sdk/sdk_flutter_example/MainActivity;'; then + echo "::error::$apk has no MainActivity class: the example's Kotlin sources were not compiled" + exit 1 + fi + + - name: Plugin unit tests (Android) + working-directory: example/android + run: ./gradlew :didit_sdk:testDebugUnitTest 2>&1 | tee "$RUNNER_TEMP/unit-tests.log" + + # Flutter names every plugin that still applies KGP and will refuse to + # build them in a future release. + - name: No KGP warning names didit_sdk + run: | + if grep -h 'plugins that apply Kotlin Gradle Plugin (KGP):.*didit_sdk' "$RUNNER_TEMP/build-apk.log" "$RUNNER_TEMP/unit-tests.log"; then + echo "::error::Flutter reports that didit_sdk applies the Kotlin Gradle Plugin on AGP 9" + exit 1 + fi + build-ios-example: name: Example app builds (iOS) runs-on: macos-latest diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 9ff1058..a908f54 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -56,7 +56,16 @@ rootProject.allprojects { plugins { id("com.android.library") - id("kotlin-android") +} + +// AGP 9+ compiles Kotlin itself ("built-in Kotlin") and rejects the Kotlin +// Gradle Plugin, and Flutter warns about plugins that declare it. Apply KGP only +// where built-in Kotlin is off: AGP 8, or AGP 9 with android.builtInKotlin=false +// (the same rule Flutter's Gradle plugin uses). +val builtInKotlin = androidComponents.pluginVersion.major >= 9 && + (providers.gradleProperty("android.builtInKotlin").orNull?.toBoolean() ?: true) +if (!builtInKotlin) { + apply(plugin = "kotlin-android") } android { @@ -69,10 +78,6 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_17.toString() - } - sourceSets { getByName("main") { java.srcDirs("src/main/kotlin") @@ -107,12 +112,22 @@ android { } } +// The `kotlin` extension exists with both KGP and built-in Kotlin, unlike +// android.kotlinOptions, which only KGP adds. +extensions.configure("kotlin") { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) + } +} + dependencies { implementation("me.didit:$diditSdkAndroidArtifact:$diditSdkAndroidVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") - testImplementation("org.jetbrains.kotlin:kotlin-test") + // KGP infers the JUnit 5 flavour of kotlin-test; built-in Kotlin does not. + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") testImplementation("io.mockk:mockk:1.14.11") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1") } diff --git a/example/android/app/build.gradle.kts b/example/android/app/build.gradle.kts index e338ea2..65aaa91 100644 --- a/example/android/app/build.gradle.kts +++ b/example/android/app/build.gradle.kts @@ -1,7 +1,9 @@ plugins { id("com.android.application") - id("kotlin-android") - // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + // No Kotlin Gradle Plugin here: on AGP 9 built-in Kotlin compiles src/main/kotlin, and on + // AGP 8 the Flutter Gradle Plugin applies the Kotlin Gradle Plugin itself (the `kotlin { }` + // block below would not resolve otherwise). CI checks the APK contains MainActivity on both. + // The Flutter Gradle Plugin must be applied after the Android Gradle Plugin. id("dev.flutter.flutter-gradle-plugin") } @@ -15,10 +17,6 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_17.toString() - } - defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId = "me.didit.sdk.sdk_flutter_example" @@ -45,6 +43,12 @@ android { } } +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 + } +} + flutter { source = "../.." } diff --git a/packages/didit_sdk_autodetection/android/build.gradle.kts b/packages/didit_sdk_autodetection/android/build.gradle.kts index 80a7512..7a2187c 100644 --- a/packages/didit_sdk_autodetection/android/build.gradle.kts +++ b/packages/didit_sdk_autodetection/android/build.gradle.kts @@ -56,7 +56,16 @@ rootProject.allprojects { plugins { id("com.android.library") - id("kotlin-android") +} + +// AGP 9+ compiles Kotlin itself ("built-in Kotlin") and rejects the Kotlin +// Gradle Plugin, and Flutter warns about plugins that declare it. Apply KGP only +// where built-in Kotlin is off: AGP 8, or AGP 9 with android.builtInKotlin=false +// (the same rule Flutter's Gradle plugin uses). +val builtInKotlin = androidComponents.pluginVersion.major >= 9 && + (providers.gradleProperty("android.builtInKotlin").orNull?.toBoolean() ?: true) +if (!builtInKotlin) { + apply(plugin = "kotlin-android") } android { @@ -69,10 +78,6 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_17.toString() - } - sourceSets { getByName("main") { java.srcDirs("src/main/kotlin") @@ -107,12 +112,22 @@ android { } } +// The `kotlin` extension exists with both KGP and built-in Kotlin, unlike +// android.kotlinOptions, which only KGP adds. +extensions.configure("kotlin") { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) + } +} + dependencies { implementation("me.didit:$diditSdkAndroidArtifact:$diditSdkAndroidVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") - testImplementation("org.jetbrains.kotlin:kotlin-test") + // KGP infers the JUnit 5 flavour of kotlin-test; built-in Kotlin does not. + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") testImplementation("io.mockk:mockk:1.14.11") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1") } diff --git a/packages/didit_sdk_core/android/build.gradle.kts b/packages/didit_sdk_core/android/build.gradle.kts index 2615be4..94c8771 100644 --- a/packages/didit_sdk_core/android/build.gradle.kts +++ b/packages/didit_sdk_core/android/build.gradle.kts @@ -56,7 +56,16 @@ rootProject.allprojects { plugins { id("com.android.library") - id("kotlin-android") +} + +// AGP 9+ compiles Kotlin itself ("built-in Kotlin") and rejects the Kotlin +// Gradle Plugin, and Flutter warns about plugins that declare it. Apply KGP only +// where built-in Kotlin is off: AGP 8, or AGP 9 with android.builtInKotlin=false +// (the same rule Flutter's Gradle plugin uses). +val builtInKotlin = androidComponents.pluginVersion.major >= 9 && + (providers.gradleProperty("android.builtInKotlin").orNull?.toBoolean() ?: true) +if (!builtInKotlin) { + apply(plugin = "kotlin-android") } android { @@ -69,10 +78,6 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_17.toString() - } - sourceSets { getByName("main") { java.srcDirs("src/main/kotlin") @@ -107,12 +112,22 @@ android { } } +// The `kotlin` extension exists with both KGP and built-in Kotlin, unlike +// android.kotlinOptions, which only KGP adds. +extensions.configure("kotlin") { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) + } +} + dependencies { implementation("me.didit:$diditSdkAndroidArtifact:$diditSdkAndroidVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") - testImplementation("org.jetbrains.kotlin:kotlin-test") + // KGP infers the JUnit 5 flavour of kotlin-test; built-in Kotlin does not. + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") testImplementation("io.mockk:mockk:1.14.11") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1") } diff --git a/packages/didit_sdk_nfc/android/build.gradle.kts b/packages/didit_sdk_nfc/android/build.gradle.kts index 90ce7d2..789f7d1 100644 --- a/packages/didit_sdk_nfc/android/build.gradle.kts +++ b/packages/didit_sdk_nfc/android/build.gradle.kts @@ -56,7 +56,16 @@ rootProject.allprojects { plugins { id("com.android.library") - id("kotlin-android") +} + +// AGP 9+ compiles Kotlin itself ("built-in Kotlin") and rejects the Kotlin +// Gradle Plugin, and Flutter warns about plugins that declare it. Apply KGP only +// where built-in Kotlin is off: AGP 8, or AGP 9 with android.builtInKotlin=false +// (the same rule Flutter's Gradle plugin uses). +val builtInKotlin = androidComponents.pluginVersion.major >= 9 && + (providers.gradleProperty("android.builtInKotlin").orNull?.toBoolean() ?: true) +if (!builtInKotlin) { + apply(plugin = "kotlin-android") } android { @@ -69,10 +78,6 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_17.toString() - } - sourceSets { getByName("main") { java.srcDirs("src/main/kotlin") @@ -107,12 +112,22 @@ android { } } +// The `kotlin` extension exists with both KGP and built-in Kotlin, unlike +// android.kotlinOptions, which only KGP adds. +extensions.configure("kotlin") { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) + } +} + dependencies { implementation("me.didit:$diditSdkAndroidArtifact:$diditSdkAndroidVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") - testImplementation("org.jetbrains.kotlin:kotlin-test") + // KGP infers the JUnit 5 flavour of kotlin-test; built-in Kotlin does not. + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") testImplementation("io.mockk:mockk:1.14.11") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1") }