3

I'm updating our project to use Gradle 4.1 and Android Gradle plugin 3.0.1. I have updated our dependency configuration to the new configuration and the project successfully compiles. However, there are lots of unresolved dependencies (incl. Kotlin standard library's top-level functions) when compiling android tests (assembleAndroidTest Gradle task). I was suspecting that Proguard might cause this (although it didn't before updating Gradle), but even adding explicit rules to keep symbols/classes doesn't help. We use Kotlin 1.2.10 and Kotlin-Kapt plugin.

I appreciate any help.

Javad
  • 5,755
  • 4
  • 41
  • 51

1 Answers1

0

I don't use ProGuard for debug but the following answers seem useful. I would revise your Gradle configuration another time following the migration guide, and first of all clean and invalidate caches.

Proguard

Chech this question and answers about how to use Kotlin with Proguard.

Disable these directives in your build.gradle file to discard Proguard.

minifyEnabled false
shrinkResources false

Configure Proguard for Kotlin.

You don't need to do anything special. Kotlin works with ProGuard out of the box. But you may face some strange errors when processing your application with ProGuard. In this case just add:

-dontwarn kotlin.**

You also can add:

-keep class kotlin.** { *; }
-keep class kotlin.Metadata { *; }
-dontwarn kotlin.**
-keepclassmembers class **$WhenMappings {
    <fields>;
}
-keepclassmembers class kotlin.Metadata {
    public <methods>;
}
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
    static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
}

Check this related questions to either enable Proguard for tests or not:

proguard gradle debug build but not the tests

Specify the Proguard file to use on the instrumentation tests.

runProguard is old. It was replaced with minifyEnabled

With minifyEnabled (and other changes in new versions of Gradle) you will may encounter issues where the Proguard config works for your debug apk but not for the instrumentation tests. The apk created for instrumentation tests will use its own proguard file, so changing your existing proguard file will have no effect.

In this case, you need to specify the proguard file to use on the instrumentation tests. It can be quite permissive because it's not affecting your debug and release builds at all.

    // inside android block
    debug {
        shrinkResources true  // removes unused graphics etc
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        testProguardFile('test-proguard-rules.pro')
    }

Android Unit Tests with proguard enabled

Add a custom proguard rules file

/project/app/proguard-test-rules.pro

# Proguard rules that are applied to your test apk/code.
-ignorewarnings

-keepattributes *Annotation*

-dontnote junit.framework.**
-dontnote junit.runner.**

-dontwarn android.test.**
-dontwarn android.support.test.**
-dontwarn org.junit.**
-dontwarn org.hamcrest.**
-dontwarn com.squareup.javawriter.JavaWriter
# Uncomment this if you use Mockito
#-dontwarn org.mockito.**
The add the following to your build.gradle for your app. To use the proguard file when testing.

/project/app/build.gradle

android {
    debug {
        minifyEnabled true
        testProguardFile 'proguard-test-rules.pro'
    }
}

Add a buidType for testing

I've solved this problem in my build by having an additional "dev" buildType where I enable proguard, but configure it to keep all code in my own package, and a few specific library classes that happen to be used from tests only. I also disable obfuscation in the dev buildType so that it can be debugged from an IDE.

For debug and release builds I use my "real" proguard settings including obfuscation and optimizations.

Use separate test modules

Separate test modules are now variant-aware. This means that specifying targetVariant is no longer necessary.

Each variant in the test module will attempt to test a matching variant in the target project. By default, test modules contain only a debug variant, but you can create new build types and new flavors to create new variants to match the tested app project. A connectedCheck task is created for each variant.

To make the test module test a different build type only, and not the debug one, use VariantFilter to disable the debug variant in the test project, as shown below:

android {
    variantFilter { variant ->
        if (variant.buildType.name.equals('debug')) {
            variant.setIgnore(true);
        }
    }
}

If you want a test module to target only certain flavors or build types of an app, you can use the matchingFallbacks property to target only the variants you want to test. This also prevents the test module from having to configure those variants for itself.


Gradle

Revise your Gradle configuration. In order to to build an Android project written in Kotlin:

  • Set up the kotlin-android gradle plugin and apply it to your project.
  • Add kotlin-stdlib dependencies.

Those actions may also be performed automatically in IntelliJ IDEA / AS by invoking the action:

Tools | Kotlin | Configure Kotlin in Project


kotlin-android

buildscript {
    ext.kotlin_version = '1.2.10'

    ...

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

kotlin-stdlib

Don't forget to configure the standard library dependency:

repositories {
    mavenCentral()
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib"
}

Revise your dependencies configuration using the migration guide.

Note: compile, provided, and apk are currently still available.

However, they will be removed in the next major release of the Android plugin.


Provide version manually

Starting with Kotlin 1.1.2, the dependencies with group org.jetbrains.kotlin are by default resolved with the version taken from the applied plugin.

You can provide the version manually using the full dependency notation like this:

compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"

Resolution strategy

You also can force the resolution strategy:

configurations.all {
    resolutionStrategy {
        force "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    }
}

As you are using Android Gradle plugin 3.0.1:

// Instead, because the new build model delays dependency resolution, you
// should query and modify the resolution strategy using the Variant API:
android {
    applicationVariants.all { variant ->
        variant.getCompileConfiguration().resolutionStrategy {
            ...
        }
        variant.runtimeConfiguration.resolutionStrategy {
            ...
        }
        variant.getAnnotationProcessorConfiguration().resolutionStrategy {
            ...
        }
    }
}

Exclude app dependencies from test configurations using the Variant API:

On previous versions of the Android plugin, you could exclude certain transitive dependencies of your app from your tests using the exclude keyword. However, with the new dependency configurations, you must do it at execution time using the Variant API:

android.testVariants.all { variant ->
    variant.getCompileConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
    variant.getRuntimeConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
}

Extended versions of the Kotlin standard library

If you're targeting JDK 7 or JDK 8, you can use extended versions of the Kotlin standard library which contain additional extension functions for APIs added in new JDK versions. Instead of kotlin-stdlib, use one of the following dependencies:

compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7"
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"

In Kotlin 1.1.x, use kotlin-stdlib-jre7 and kotlin-stdlib-jre8 instead.


Kotlin reflection

If your project uses Kotlin reflection or testing facilities, you need to add the corresponding dependencies as well:

compile "org.jetbrains.kotlin:kotlin-reflect"
testCompile "org.jetbrains.kotlin:kotlin-test"
testCompile "org.jetbrains.kotlin:kotlin-test-junit"

Kapt

See the description of Kotlin annotation processing tool (kapt).

Apply the kotlin-kapt Gradle plugin:

apply plugin: 'kotlin-kapt'
albodelu
  • 7,931
  • 7
  • 41
  • 84