Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multi-flavor compile dependencies in gradle

Is there a way to have compile dependencies by multiple flavors in Android Studio (build.gradle)?

I have 2 flavorGroups, and in each 2 variants. Out of the 4 possible combinations I would like to be able to depend on a lib only if I'm both in latest and in free flavor. latestCompile or freeCompile works, but latestFreeCompile doesn't. this is the relevant part of my build.gradle:

android {
    defaultConfig {
        minSdkVersion 7
        targetSdkVersion 19
        versionCode 15
        versionName "1.9." + versionCode
    }

    flavorGroups 'sdk', 'cost'

    productFlavors {
        latest {
            flavorGroup 'sdk'
            minSdkVersion 8
        }
        sdk7 {
            flavorGroup 'sdk'
            minSdkVersion 7
            versionName android.defaultConfig.versionName + ".sdk7"
        }
        free {
            flavorGroup 'cost'
        }
        pro {
            flavorGroup 'cost'
        }
    }
}
dependencies {
    // this works:
    freeCompile files('libs/StartAppInApp-2.2.1.jar')

    // and I would like something like this:
    latestFreeCompile 'com.google.android.gms:play-services:4.1.32' // minSdkVersion:8
}

If I would use:

latestCompile 'com.google.android.gms:play-services:4.1.32'

then it would be included in latestPro as well (not needed) and if I'd use:

freeCompile 'com.google.android.gms:play-services:4.1.32'

then it would be included in sdk7Free as well (although it needs SDK 8)

like image 727
Gavriel Avatar asked Mar 10 '14 23:03

Gavriel


People also ask

How would you define different dependencies for different product flavors?

To define a flavor specific dependency you can use proCompile instead of compile in your dependency section. When you run gradle properties you get an overview of automatic created configurations.

What is Buildtype in Gradle Android?

A build type determines how an app is packaged. By default, the Android plug-in for Gradle supports two different types of builds: debug and release . Both can be configured inside the buildTypes block inside of the module build file.

How do I sync Gradle with dependencies?

Simply open the gradle tab (can be located on the right) and right-click on the parent in the list (should be called "Android"), then select "Refresh dependencies". This should resolve your issue.

What is testImplementation in Gradle?

Configuration inheritance and composition For example the testImplementation configuration extends the implementation configuration. The configuration hierarchy has a practical purpose: compiling tests requires the dependencies of the source code under test on top of the dependencies needed write the test class.


3 Answers

As now described on the official Android Developers website, if you want to declare a dependency for a specific combination of product flavor AND build type you need to declare that configuration first.

For example for the free flavor and the debug type:

configurations {
    freeDebugImplementation {}
}

dependencies {
    freeDebugImplementation 'com.google.firebase:firebase-ads:9.8.0'
}

Advanced: multiple flavor dimensions

Note that if your app has multiple flavor dimensions, you must create the configuration corresponding to the full build variant (ALL flavors, then build type), like so:

flavorDimensions "money", "image" 
productFlavors {
    free { dimension "money" }
    paid { dimension "money" }

    picasso { dimension "image" }
    glide { dimension "image" }
}

configurations {
    freePicassoDebugImplementation {}
    freeGlideDebugImplementation {}
}

dependencies {
    freePicassoDebugImplementation 'com.google.firebase:firebase-ads:9.8.0'
    freeGlideDebugImplementation 'com.google.firebase:firebase-ads:9.8.0'
}
like image 143
David Ferrand Avatar answered Sep 18 '22 19:09

David Ferrand


I had the same problem. I resolved that with some gradle code in my build.gradle:

// global variables
ext {
    buildType = ""
    groupCost = ""
    groupSdk = ""
}

def splitCamelCase(String word) {
    def result = []
    int nextStart = 0;
    for (int i = 1; i < word.length(); i++) {
        if(word.charAt(i).isUpperCase()) {
            result.add(word.substring(nextStart, i));
            nextStart = i;
        }
    }

    result.add(word.substring(nextStart));
    return result;
}

// start parameters
println "Start parametes: tasks = " + gradle.startParameter.getTaskNames()

gradle.startParameter.getTaskNames().each { task ->

    // This line is needed to skip other projects' tasks
    // You can safely remove it if you have only one project
    if(!task.startsWith(':<your_application_name>:')) return;

    def taskParts = splitCamelCase(task.split(":").last());
    def groupCostPrefix = taskParts[taskParts.size() - 3];
    def groupSdkPrefix = taskParts[taskParts.size() - 2];
    def buildTypePrefix = taskParts[taskParts.size() - 1];

    if("Debug".startsWith(buildTypePrefix)) {
        buildType = 'debug';
    }
    else if("Release".startsWith(buildTypePrefix)) {
        buildType = 'release';
    }
    else {
        return; // do not process tasks that are not ending with proper build type.
    }

    if("Free".startsWith(groupCostPrefix)) {
        groupCost = 'free';
    }
    else if("Pro".startsWith(groupCostPrefix)) {
        groupCost = 'pro';
    }

    if("Sdk7".startsWith(groupSdkPrefix)) {
        groupSdk = 'froyo';
    }
    else if("Latest".startsWith(groupSdkPrefix)) {
        groupSdk = 'latest';
    }
}

Then all you need is to add following code inside your 'dependencies' section:

if(groupSdk == 'latest' && groupCost == 'free') {
    compile 'com.google.android.gms:play-services:4.1.32'
}
like image 28
Paweł Stankowski Avatar answered Sep 19 '22 19:09

Paweł Stankowski


Same issue here, but Pawel's solution didn't work because gradle dependencies have other issues in which it starts building not only the selected flavors/build type, but all of them and it requires a more dynamic solution.

Still I found this issue tracker: https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&groupby=&sort=&id=52962

There is also a reference to fixing the rebuild-all bug I mentioned above, but I didn't try it yet.

And implemented this solution (as per issue tracker reply #60):

    Map<String, Dependency> customDeps = new HashMap<String, Dependency>()

    customDeps.put('flavor1GrpXflabor1GrpYDebugCompile', dependencies.project(path: ':lib', configuration: 'debug'))
    customDeps.put('flavor1GrpXflavor1GrpYReleaseCompile', dependencies.project(path: ':lib', configuration: 'release'))

    customDeps.put('flavor2GrpXflavor1GrpYDebugCompile', dependencies.project(path: ':other_lib', configuration: 'debug'))
    customDeps.put('flavor2GrpXflavor1GrpYReleaseCompile', dependencies.project(path: ':other_lib', configuration: 'release'))

    ....

    configurations.all() { config ->
        Dependency d = customDeps.get(config.name)
        if (d != null) {
            config.dependencies.add(d)
        }
    }
like image 42
3c71 Avatar answered Sep 20 '22 19:09

3c71