Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional dependencies with Gradle, is it possible?

I have an Android project (already ported to Android Studio and using Gradle) that is made up of different modules.

The project is actually used to create two different apps, where the code is pretty much the same, except for some resources.

Thus the resources have been split into two different modules.

The original author of this project used to work in Eclipse and switch the resource modules included in the dependencies based on which app he wanted to build. And he also used to change by hand the package name in AndroidManifest.xml

I would like to automate all of this and still have a single code base, but have two build targets with specific modules for each target. Is that doable with Gradle?

Update:

To make things even harder, my project has a hierarchy that is pretty much the following:

--+--MainProject
  +--LibData
  +--LibBase
  +--LibResA
  +--LibResB

Where:

  • MainProject depends on LibBase and LibData.
  • LibData depends on LibBase
  • LibBase either depends on LibResA or LibResB based on the final APK that I need to build.

As suggested, I've tried implementing this with flavors by adding in the MainProject build.gradle the following:

productFlavors {
    producta {
    }
    productb {
    }
}

And then in LibBase I've added the following to its build.gradle:

dependencies {
    productaCompile project(':LibResA')
    productbCompile project(':LibResB')
}

But then, when I build the project, LibData can't find the classes and resources inherited from LibBase. So now I'm stuck with this error. To me it looks like LibBase isn't being copied to the intermediates of LibData. That way LibData can't resolve the classes in LibBase, but it's just my assumption.

Update 2:

I kept investigating this issue and now I've changed my build.gradle files to look like this:

Main Project build.gradle:

defaultPublishConfig "productaRelease"
publishNonDefault true

productFlavors {
    producta {
        applicationId "com.producta"
    }

    productb {
        applicationId "com.productb"
    }
}

dependencies {
    compile project(':LibData')
}

LibData build.gradle (has no product flavors, just the dependencies):

dependencies {
    compile project(':LibBase')
}

LibBase build.gradle:

defaultPublishConfig "productaRelease"
publishNonDefault true

productFlavors {
    producta {
    }

    productb {
    }
}

dependencies {
    productaCompile project(path: ':LibResA')
    productbCompile project(path: ':LibResB')
}

This way I get no errors when doing the usual gradle clean build but I can see that the resources included are always those of LibResA just like the defaultPublishConfig is the only one used at all times.

If I open this project in Android Studio (0.8.1 atm) the result is that if I try to switch the build variant of the LibBase module and set it to productbRelease, the following error is being shown: Error:Module 'LibBase' has variant 'productbRelease' selected, but the module ''LibData'' depends on variant 'productaRelease'.

I'm running out of ideas.

like image 492
Valerio Santinelli Avatar asked Jun 25 '14 14:06

Valerio Santinelli


3 Answers

Since you already have the product flavors:

productFlavors {
    producta {
    }

    productb {
    }
}

Define your dependencies prefixed with flavor name. Example:

dependencies {
    productaImplementation 'com.google.android.gms:play-services:11.0.2'
    productbImplementation 'com.google.android.gms:play-services:12.0.1'
}

Common dependencies will be defined normally.

Now build apk for individual flavors.

like image 130
user846316 Avatar answered Sep 21 '22 05:09

user846316


Not the best way to do it, but if productFlavors is not enough to specify conditional dependencies you can rely on an inline if and evaluate it based on some value that can be injected via external properties.

For example here is how I toggle LeakCanary (no-op is just the empty implementation of the other one):

build.gradle

dependencies {
    compile "com.squareup.leakcanary:leakcanary-android"+(project.ext.has("leakCanary")?"":"-no-op")+":1.3.1"
}

To build with com.squareup.leakcanary:leakcanary-android:1.3.1:

$ ./gradlew :app:assembleDebug -PleakCanary

By default it builds with the empty implementation com.squareup.leakcanary:leakcanary-android-no-op:1.3.1:

$ ./gradlew :app:assembleDebug

This provides a quick and more flexible way to toggle things using build command, but too much of it and things will get messy real quick.

like image 21
hidro Avatar answered Sep 22 '22 05:09

hidro


Yes, it is. New Android build system based on Gradle supports your use case with its concept of product flavors. http://tools.android.com/tech-docs/new-build-system/user-guide

Note that you will likely want to switch from Eclipse to Android Studio when you do migration to Gradle build.

like image 26
Radim Avatar answered Sep 19 '22 05:09

Radim