1

I'm trying to make my app to build again after updating from google gradle plugin 3.1.4 to the now latest 3.2.1 and I think there maybe something wrong with the approach I took to make my instantapp work in first place, so I post this question to discard or confirm that, and in such case to ask for a different approach.

Briefly, my app is a game with many graphics in the assets folder. To make the instantapp fit into the 10 megabyte limit imposed by Google Play, I had to remove some of these assets that I won't need in the instant version and also those with higher resolution, while keeping the rest. Also the instantapp has slightly different code than the installed one, so I override some methods and classes there.

The minimal com.android.instantapp module cannot contain assets and code, it seemed, so I created an intermediary feature module which hold these instead.

So, after a lot of struggle (mainly with firebase and the com.google.gms.google-services plugins) I came up with this approach that eventually worked with the version 3.1.4 of the gradle plugin (com.android.tools.build:gradle:3.1.4)

my current approach

The only weird thing there, is that I had to set the flag "baseFeature" to both, the android_instant_feature and the android_common feature modules. But it worked and I could finally publish an instant version of my game, while the installed version continued building just fine too.

Now, as soon as I update to version 3.2.1 of the google gradle plugin, the problems begins even if I do not change anything else. I got a lot of errors of this sort when trying to build the project:

Unable to find a matching variant of project :android_common:
  - Variant 'debugApiElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found compatible value 'debug'.
      - Found com.android.build.api.attributes.VariantAttr 'debug' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Aar'.
      - Found org.gradle.usage 'java-api' but wasn't required.
  - Variant 'debugFeatureApiElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found compatible value 'debug'.
      - Found com.android.build.api.attributes.VariantAttr 'debugFeature' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Feature'.
      - Found org.gradle.usage 'java-api' but wasn't required.
  - Variant 'debugFeatureRuntimeElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found compatible value 'debug'.
      - Found com.android.build.api.attributes.VariantAttr 'debugFeature' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Feature'.
      - Found org.gradle.usage 'java-runtime' but wasn't required.
  - Variant 'debugRuntimeElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found compatible value 'debug'.
      - Found com.android.build.api.attributes.VariantAttr 'debug' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Aar'.
      - Found org.gradle.usage 'java-runtime' but wasn't required.
  - Variant 'releaseApiElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found incompatible value 'release'.
      - Found com.android.build.api.attributes.VariantAttr 'release' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Aar'.
      - Found org.gradle.usage 'java-api' but wasn't required.
  - Variant 'releaseFeatureApiElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found incompatible value 'release'.
      - Found com.android.build.api.attributes.VariantAttr 'releaseFeature' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Feature'.
      - Found org.gradle.usage 'java-api' but wasn't required.
  - Variant 'releaseFeatureRuntimeElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found incompatible value 'release'.
      - Found com.android.build.api.attributes.VariantAttr 'releaseFeature' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Feature'.
      - Found org.gradle.usage 'java-runtime' but wasn't required.
  - Variant 'releaseRuntimeElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found incompatible value 'release'.
      - Found com.android.build.api.attributes.VariantAttr 'release' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Aar'.
      - Found org.gradle.usage 'java-runtime' but wasn't required.

After some tweaking with the gradle scripts, including removing the baseFeature true flag from the android_instant_feature module, I got a briefer error, but I cannot really say whether I'm better off than before. The error is this:

Expected configuration ':android_instant_feature:debugFeatureCompileClasspath' to contain exactly one file, however, it contains no files.

And now I'm just blocked here without knowing what to do or where to look next, so I wonder if there's something fundamentally flawed with my approach in first place, since I came up with it after some trial and error and I'm not sure it's fine, and in such case which other approach I could use that fulfills my requirements and those from Google, whichever they are.

These are my Gradle Files for these modules, as per request of @TWL. I have replaced the real app id with com.myapp.id, the rest is exactly what I have, including a lot of stuff for libGDX that I think is irrelevant for the matter, though I prefer to leave it there in the name of accuracy:

android_common:

apply plugin: "com.android.feature"

configurations { natives }

android {
    baseFeature true
    compileSdkVersion 28
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
            jniLibs.srcDirs = ['libs']
        }

        androidTest.setRoot('tests')
    }
    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        sourceSets {
            all {
                manifest.srcFile "AndroidManifest.xml"
            }
        }
        multiDexEnabled true
    }
    buildTypes {
        release {
            setMinifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    buildToolsVersion '28.0.2'
    lintOptions {
        abortOnError false
    }
    dexOptions {
        jumboMode true
    }
}

dependencies {
    application project(':android')
    implementation project(':core')
    api 'com.android.support:multidex:1.0.3'
    implementation "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion"
    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi"
    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a"
    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-arm64-v8a"
    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86"
    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64"
    implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
    natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi"
    natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi-v7a"
    natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-arm64-v8a"
    natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86"
    natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86_64"
    implementation "com.android.billingclient:billing:$playBillingLibVersion"
    implementation "com.google.firebase:firebase-core:16.0.3"
    implementation "com.google.firebase:firebase-ads:15.0.1"

    implementation fileTree(include: ['*.jar'], dir: 'libs')
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'com.google.guava:guava:24.0-android'
}

task copyAndroidNatives() {
    file("libs/armeabi/").mkdirs();
    file("libs/armeabi-v7a/").mkdirs();
    file("libs/arm64-v8a/").mkdirs();
    file("libs/x86_64/").mkdirs();
    file("libs/x86/").mkdirs();

    configurations.natives.files.each { jar ->
        def outputDir = null
        if (jar.name.endsWith("natives-arm64-v8a.jar")) outputDir = file("libs/arm64-v8a")
        if (jar.name.endsWith("natives-armeabi-v7a.jar")) outputDir = file("libs/armeabi-v7a")
        if (jar.name.endsWith("natives-armeabi.jar")) outputDir = file("libs/armeabi")
        if (jar.name.endsWith("natives-x86_64.jar")) outputDir = file("libs/x86_64")
        if (jar.name.endsWith("natives-x86.jar")) outputDir = file("libs/x86")
        if (outputDir != null) {
            copy {
                from zipTree(jar)
                into outputDir
                include "*.so"
            }
        }
    }
}

android_instant_feature:

apply plugin: "com.android.feature"

android {
//    baseFeature true
    buildToolsVersion "28.0.2"
    compileSdkVersion 28
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ["${project(':android_common').projectDir}/res"]
            assets.srcDirs = ['assets']
            jniLibs.srcDirs = ['libs']
        }
    }
    defaultConfig {
        versionCode 4031
        versionName "4.1.2.1i"
        minSdkVersion 16
        targetSdkVersion 28
        multiDexEnabled true
    }

    dexOptions {
        jumboMode true
    }

    // Proguard configuration
    buildTypes {
        release {
            //minifyEnabled true will turn proguard ON
//            minifyEnabled true
            //proguardFiles let you add your own proguard rules ('proguard-project.txt') in this case, as its already created by gdx-setup
//            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
        }
        debug {}
    }


    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    productFlavors {
    }
    lintOptions {
        abortOnError false
    }
}

task run(type: Exec) {
    def path
    def localProperties = project.file("../local.properties")
    if (localProperties.exists()) {
        Properties properties = new Properties()
        localProperties.withInputStream { instr ->
            properties.load(instr)
        }
        def sdkDir = properties.getProperty('sdk.dir')
        if (sdkDir) {
            path = sdkDir
        } else {
            path = "$System.env.ANDROID_HOME"
        }
    } else {
        path = "$System.env.ANDROID_HOME"
    }

    def adb = path + "/platform-tools/adb"
    commandLine "$adb", 'shell', 'am', 'start', '-n', 'com.marzoa.ruletafree/com.marzoa.ruletafree.AndroidLauncher'
}

dependencies {
    implementation project(":core")
    feature project(":android_common")
    api 'com.android.support:multidex:1.0.3'
    implementation "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion"
    implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
    implementation "com.google.firebase:firebase-core:16.0.3"
    implementation 'com.google.android.gms:play-services-instantapps:16.0.0'

    implementation fileTree(dir: 'libs', include: ['*.jar'])
}

// ADD THIS AT THE BOTTOM
apply plugin: 'com.google.gms.google-services'

android_instant:

apply plugin: 'com.android.instantapp'

android {
    defaultConfig {
        applicationId "com.myapp.id"
        minSdkVersion 16
        targetSdkVersion 28
        multiDexEnabled true
    }
}

dependencies {
    implementation project(":android_common")
    implementation project(":android_instant_feature")
}

android:

apply plugin: "com.android.application"
apply plugin: 'io.fabric' // Needed by crashlytics. DO NOT REMOVE.

android {
    buildToolsVersion "28.0.2"
    compileSdkVersion 28
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ["${project(':android_common').projectDir}/res"]
            assets.srcDirs = ['assets']
            jniLibs.srcDirs = ['libs']
        }

        androidTest.setRoot('tests')
    }
    defaultConfig {
        applicationId "com.myapp.id"
        versionCode 4032
        versionName "4.1.2.1"
        minSdkVersion 16
        targetSdkVersion 28
        multiDexEnabled true
    }
    dexOptions {
        jumboMode true
    }
    // Proguard configuration
    buildTypes {
        release {
            //minifyEnabled true will turn proguard ON
            minifyEnabled true
            //proguardFiles let you add your own proguard rules ('proguard-project.txt') in this case, as its already created by gdx-setup
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    productFlavors {
    }
    lintOptions {
        abortOnError false
    }
}

task importPuzzles(type: Exec) {
    // TODO
}

task run(type: Exec) {
    def path
    def localProperties = project.file("../local.properties")
    if (localProperties.exists()) {
        Properties properties = new Properties()
        localProperties.withInputStream { instr ->
            properties.load(instr)
        }
        def sdkDir = properties.getProperty('sdk.dir')
        if (sdkDir) {
            path = sdkDir
        } else {
            path = "$System.env.ANDROID_HOME"
        }
    } else {
        path = "$System.env.ANDROID_HOME"
    }

    def adb = path + "/platform-tools/adb"
    commandLine "$adb", 'shell', 'am', 'start', '-n', 'com.marzoa.ruletafree/com.myapp.id.AndroidLauncher'
}

dependencies {
    implementation project(":core")
    implementation project(":android_common")
    api 'com.android.support:multidex:1.0.3'
    implementation "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion"
    implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
    implementation "com.android.billingclient:billing:$playBillingLibVersion"
    implementation "com.google.firebase:firebase-core:16.0.3"
    implementation "com.google.firebase:firebase-ads:15.0.1"
    implementation 'com.crashlytics.sdk.android:crashlytics:2.9.5'

    implementation fileTree(include: ['*.jar'], dir: 'libs')
}

// ADD THIS AT THE BOTTOM
apply plugin: 'com.google.gms.google-services'

Thanks a lot in advance!

Fran Marzoa
  • 4,293
  • 1
  • 37
  • 53
  • This is curious, I was only able to reproduce your "new error" if I left the com.android.instantapp module empty of dependencies..., but of course, there should only be one `baseFeature`, but the issue is probably with how you configured your gradle files to tie together the structure/dependencies of your instant/app, can you show them? – TWL Nov 05 '18 at 22:41
  • Also reproducible if the instantapp module did not include the `base` – TWL Nov 05 '18 at 22:47
  • Thanks a lot for answering, @TWL. The dependencies of my android_instant instantapp module aren't blank, though it has just two of them: the android_common module and the android_instant_feature one. The rest of the needed stuff is already in android_common or android_instant_feature. It's late at night here, but I promise you I'll upload the skimmed gradle files tomorrow. – Fran Marzoa Nov 06 '18 at 00:39
  • @TWL I have updated my question adding the Gradle files. Please note that I have replaced the real app id with com.myapp.id instead. Bests! – Fran Marzoa Nov 06 '18 at 10:56

1 Answers1

1

Thanks for providing the gradle files and looks like your gradle dependencies were mixed up.

The com.android.feature module that holds the baseFeature true should be the one whose dependencies also hold both the application project() and feature project(). That’s the main cause of your error:

Expected configuration ':___:debugFeatureCompileClasspath' to contain exactly one file, however, it contains no files.

So from what you provided, it looks like your application project() and feature project() are separated.

To fix this, your “baseandroid_common should be:

application project(':android')
feature project(":android_instant_feature")

And your “non-base featureandroid_instant_feature should be:

implementation project(':android_common)

This won’t change the overall structure of your project.

  • Your com.android.application will be compiled with the libraries: core and android_common.
  • Your com.android.instantapp will be compiled with the instant app apks: android_common.apk and android_instant_feature.apk.

Now, why did it work with 2x baseFeature before ? I don’t know, it shouldn’t have...

There used to be a page @ https://g.co/instantapps that explained the structure of instant apps, but you can refer to one of my previous posts that touches on it for an overview and reference links: How can i access an activity from another feature module

Side note: I noticed that you have a implementation project(":core") common throughout your modules (minus the instantapp). If that is purely a library like com.android.library, then you shouldn’t have to set it up like that. You can actually just place it in your base as: api project(":core") and removing all other references elsewhere. Declaring it as api means that any module that also implements the base will also be exposed to this dependency. (Try it, there are some rare cases where this is bugged depending on what other libs your core has, but try and see).

TWL
  • 6,228
  • 29
  • 65
  • Thanks a million! This is working. :) To add to your answer, I had to change minSDKVersion too, because I have some places with 15 and others with 16, which is obviously a mistake and unrelated to my previous problems. Again, many thanks. – Fran Marzoa Nov 07 '18 at 16:18