38

I want to dynamically add a dependency in an Android Gradle project based on the current buildType. I know I can specify the buildType in the dependency:

compile project(path: ':lib1', configuration: 'debug')

But how can I use the current buildType to specify which variant of the library I want to import, so that a debug or release build automatically imports the debug or release variant of the library? What I want is something like this (where currentBuildType is a variable containing the name of the currently used buildType):

compile project(path: ':lib1', configuration: currentBuildType)

The library project I want to import has set publishNonDefault true, so all buildTypes are published.

Arthur Dent
  • 1,248
  • 2
  • 12
  • 23
  • possible duplicate of [Build variants in Gradle for a Library Project in Android](http://stackoverflow.com/questions/17451803/build-variants-in-gradle-for-a-library-project-in-android) – Scott Barta Sep 09 '14 at 15:46

8 Answers8

24

You can use

if (gradle.startParameter.taskNames.contains("assembleExample")) {
    // do stuff
}

That variable will be set before the buildConfig block is evaluated

Pavel
  • 5,374
  • 4
  • 30
  • 55
Shooky
  • 1,269
  • 8
  • 16
  • you should take a look at all of your tasks for the active config to ensure you've got the right task name defined as it has a couple variations depending on how your project is setup. System.out.println(gradle.startParameter.taskNames) should do the trick. Then look for that task in @Shooky's example above. – ShellDude Dec 27 '17 at 16:58
  • 8
    @ShellDude That returned an empty list for me but def isDebug = gradle.startParameter.taskRequests.any {it.args[0].contains("Debug")} did the trick – JLund Nov 28 '18 at 09:24
  • I like that method better... may even convert to it. I wonder if we're seeing a difference in gradle versions or build config here. There's probably some subtle difference between taskRequests and taskNames. – ShellDude Nov 28 '18 at 16:45
  • This is the best answer in all of stackoverflow for this. Every other answer tells you to create another task and check a dependency graph and my skin starts to itch, all you need to do is check an argument. Thanks! – Gubatron May 04 '19 at 19:35
  • You are a effing genius! I've searched nearly 20 different answers to this same question and this is the only answer that actually makes sense. **UPVOTE THIS TO THE TOP!** – Joshua Pinter Aug 13 '19 at 15:35
  • 1
    However, one complaint. This doesn't work when in Android Studio and you just do a rebuild or re-sync your Gradle. There is no task name arguments. – Joshua Pinter Aug 13 '19 at 15:44
  • 2
    @JLund More like def isDebug = gradle.startParameter.taskRequests.any { !it.args.isEmpty() && it.args.first().contains("Debug") } – RealMan Nov 20 '19 at 08:15
14

I could not find a clean way to get the current build type during the configuration phase of Gradle. Instead I define the dependency for each build type separately like that:

debugCompile project(path: ':lib1', configuration: 'debug')
releaseCompile project(path: ':lib1', configuration: 'release')

If you have many build types and many project dependencies this can get very verbose, but it is possible to add a function to make the dependency a one-liner. You need to add this to your main Gradle build file:

subprojects {
    android {
        dependencies.metaClass.allCompile { dependency ->
            buildTypes.each { buildType ->
                "${buildType.name}Compile" project(path: ":${dependency.name}", configuration: buildType.name)
            }
        }
    }
}

Then you can add project dependencies in your Gradle modules like this:

allCompile project(':lib1')

If you also use build flavors you would have to adapt the solution. See this link for a documentation of the feature: http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication

Please note that the Android team is working on an improvement for this behaviour: https://code.google.com/p/android/issues/detail?id=52962

Arthur Dent
  • 1,248
  • 2
  • 12
  • 23
  • Adding your allCompile to my android class works fine, but adding it to my root gradle file doesn't work. It seems that gets applied before the android plugin, and as such it breaks :/ – neuron Nov 10 '15 at 13:59
  • @neuron Try change `subprojects` to `allprojects`. From docs: 'Configures this project and each of its sub-projects. This method executes the given closure against this project and its sub-projects.' – Vsevolod Ganin Jan 15 '16 at 16:15
  • As it is not obvious how this works for the combination buildType and flavor, here is the solution: Because the configuration 'ReleaseCompile' or 'DebugCompile' is not known to gradle in the evaluation phase, these have to be added also dynamically: allprojects { configurations.metaClass.addFlavorConfiguration { def configName = "$flavor.name${buildType.name.capitalize()}Compile"; create(configName) } } } } – Adreamus Nov 21 '16 at 14:01
12

Add a task which depends on each assembleXxx task and property setting up after it invoked

ext {
    currentConfig = ""
}
task generateConfigProperty(dependsOn: 'installDebug') {

    android.applicationVariants.all { variant ->
        variant.outputs.each { output ->

            def taskName = "taskindicate$output.name"
            task "$taskName"() << {
                project.ext.set("currentConfig", "$output.name")
            }
            output.assemble.dependsOn "taskindicate$output.name"
        }
    }

}

task getConfig(dependsOn: ['installDebug', 'generateConfigProperty']) << {
    println("My config is $currentConfig")
}

took idea from the answer

Alexander Blinov
  • 393
  • 3
  • 13
  • 4
    +1 Note that if you are using the `com.android.library` plugin, `applicationVariants` is not an accessible property. It becomes `libraryVariants` instead. http://stackoverflow.com/questions/28547203/module-dependency-android-studio – Mapsy May 08 '17 at 09:32
6

This one is quite simple:

android {
    applicationVariants.all { variant ->
        variant.buildType.name // this is the value!
    }
}

Edit: Apparently at some point with gradle update, this is not working as I mentioned in a comment below. So I'd recommend checking other options.

Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
Reinherd
  • 5,476
  • 7
  • 51
  • 88
2

Based on Shooky's answer and JLund's comment, this is what works for me. I have this code in my app build.gradle towards the end:

ext.getCurrentBuildType = {
    def isDebug = gradle.startParameter.taskRequests.any {
                            it.args.any { it.endsWith("Debug") } }
    return isDebug ? "Debug" : "Release"
}

Just call it afterEvaluate. Example:

afterEvaluate {
    tasks.getByName("externalNativeBuild${ext.getCurrentBuildType()}")
         .dependsOn myOtherTask
}

Apparently, gradle.startParameter has plenty of info about current run. Take a look at the doc.

rpattabi
  • 9,984
  • 5
  • 45
  • 53
1
// declare a custom task class so you can reuse it for the different
// variants
class MyTask extends DefaultTask {
     String mVariantName;
     public void setVariantName(String variant) {mVariantName = variant;}
     public String getVariantName() { return mVariantName; }
     @TaskAction
     void action(){
        // do stuff
     }
}

// put this after your `android{}` block.
android.applicationVariants.all { variant ->
    def taskName = "myTask_$variant.name"
    task "$taskName"(type: MyTask) << {
        // you can setup this task to various info regarding
        // variant
        variantName = variant.name
    }
    variant.assemble.dependsOn (taskName)
}

See Advance customization for more details of what you can extract from the variant variable.

now you it will properly hookup your MyTask into to the chain. Doing it this way should also cleanly handle building multiple flavors all at once as it creates a new instance of MyTask for each variant.

what
  • 11
  • 1
-3

The idea of executing a certain code based on the environment should be through dependency injection.


Take into account to use the following declaration only for exceptional configurations because in general it should not happen that in the code there are conditionals that make references to the environments.

build.gradle

android {

    def TRUE = "true"
    def FALSE = "false"
    def IS_DEV = "IS_DEV"
    def IS_RELEASE = "IS_RELEASE"

    defaultConfig {
        //...
        buildConfigField BOOLEAN, IS_DEV, FALSE
        buildConfigField BOOLEAN, IS_RELEASE, FALSE
    }

    buildTypes {
        debug {
            buildConfigField BOOLEAN, IS_DEV, TRUE
        }
        release {
            buildConfigField BOOLEAN, IS_RELEASE, TRUE
        }
    }

}

GL

Braian Coronel
  • 22,105
  • 4
  • 57
  • 62
-3

Running Gradle task 'assembleRelease'...

X Fa
  • 1
  • 1