4

I'm working in Android Studio 0.8.6

End Goal

Have 3 variants - Noopt, Debug, Release - that set a variable's value based on which variant is being built. For example, if I was building a Noopt build, the string mode should be equal to noopt.

Current Implementation

Here's what I'm trying right now.

buildTypes {
    release {
        debuggable false
        runProguard false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        signingConfig signingConfigs.packageRelease
        mode = "release"
        println("In RELEASE")
    }
    noopt {
        debuggable true
        jniDebugBuild true
        renderscriptDebugBuild false
        runProguard false
        zipAlign true
        mode = "noopt"
        println("In NOOPT")
    }

}

I declare mode outside of android by doing

def mode = ""

However, no matter what variant I build with, mode is always set to noopt. If I add more variants, the variable is always set to whatever the last variant is.

Is my understanding of how gradle works incorrect? I would have assumed that it would only run the code for the variant you're building, but it appears to run it for every variant - or at least any non-standard Android properties/code get run no matter what.

Is there some other way I should be doing this?

Edit:

To add some more context, here's what I want in the end:

task runCustomScript(type:Exec) {
    def mode = ""
    if (currentBuildType == debug)
        mode = "DEBUG=1"
    else if (currentBuildType == noopt)
        mode = "NOOPT=1"

    /* etc etc etc */


    executable "myExec"
    args "-C", "blahblahblah", mode
}

So what I need is a way to find the current variant or build type being run from within a task.

Community
  • 1
  • 1

1 Answers1

2

Is my understanding of how gradle works incorrect?

Presumably, yes.

I would have assumed that it would only run the code for the variant you're building

No.

but it appears to run it for every variant

Yes, because the code that you are running is building a data structure that represents the task model. Only after build.gradle is interpreted are tasks run. Think of build.gradle as being like an XML file or a JSON file, but one that just happens to be implemented as a domain-specific language (DSL) on top of the Groovy scripting language.

Is there some other way I should be doing this?

Yes, though since it is unclear what mode is for, it is difficult to tell you how. You might consider asking a separate Stack Overflow question explaining what you are really trying to accomplish, so that we can help you determine a solution.

=====

You need to generate N tasks, one for each variant. For example, this snippet of a build.gradle file creates custom jar* tasks for all non-debug build variants:

// from http://stackoverflow.com/a/19484146/115145
android.libraryVariants.all { variant ->
  def name = variant.buildType.name
  if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
    return; // Skip debug builds.
  }
  def task = project.tasks.create "jar${name.capitalize()}", Jar
  task.dependsOn variant.javaCompile
  task.from variant.javaCompile.destinationDir
  task.archiveName = "cwac-${task.archiveName}"
}

I have not created a series of Exec tasks, but I assume it would translate into something like this:

android.libraryVariants.all { variant ->
  def name = variant.buildType.name
  def task = project.tasks.create "runCustomScript${name.capitalize()}", Exec
  // you probably need something here for task.dependsOn variant.javaCompile
  task.executable "myExec"
  // derive mode in here based on variant
  task.args "-C", "blahblahblah", mode
}

That would give you runCustomScriptNoopt, runCustomScriptDebug, and runCustomScriptRelease tasks.

Most likely, you will want to use task.dependsOn to determine what other task must be done first before this task can be run.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Part of my build runs a custom shell script by using an Exec task. When I run this script, I need to pass in an argument (like the string 'RELEASE=1'). My goal here is to set this string based on which variant I'm building. I also tried using `android.applicationVariants.all` but of course this also loops through all the available variants. I guess what I really need is a way to detect the current variant being built from within gradle. – Matthew Critelli Sep 06 '14 at 00:17
  • Did you find any solutions yet @MatthewCritelli? – guydemossyrock Dec 05 '14 at 10:48
  • Sorry for the super delayed response, but I never found a good way to do this - I just told my fellow engineers to manually replace the arguments as needed. – Matthew Critelli Jan 13 '15 at 22:16
  • I am trying to do something very close to this. I just need to pass in whether the variant is in debug or release to a task that is kicked off prior to compiling that builds a required .so file (JNI build) all the needs to know is if the build variant is debug or release. I've picked at this for months now with no clear solution to be found. Why can't a task know which variant is currently selected before any [commandLine]'s are fired off I am really tied of manually syncing what build type variant... Its bungled a release and made a mess of things. Its time robots do the work. – Hunter-Orionnoir May 16 '16 at 05:20
  • @Hunter: "Why can't a task know which variant is currently selected before any [commandLine]'s are fired off" -- a task does know which variant is currently selected, if that task was generated specifically for that variant. Please understand that `build.gradle` does not build your app. It builds an object model of *how* to build your app. IDEs and other tools then use that object model to accomplish their aims (e.g., build the app when the user clicks "Run"). If you do not like Gradle, use a different build system. Or create your own. – CommonsWare May 16 '16 at 10:49
  • I like gradle a lot. I am an immigrant/refugee from ADT using ant and eclipse. I don't know how to get gradle to define a variable that will get picked up when that commandLine is fired off. If I use defines and manually set them (requiring) a sync to change build types it does what I want to accomplish before it builds, but that is fraught with human error and I've botched many a build. I will be the first to admit I do not know gradle, much of it is scripting black magic glommed together from people's solutions to problems on SO. What's the right way or the right questions to ask? – Hunter-Orionnoir May 17 '16 at 03:29
  • also late, but what we do we create folders that are named after the buildVariant in the src folder (Anroid studio even has support for this, you can create a new folder there and select the build variant it belongs to). In these folders we create property files (xml for example). They get picked up at runtime from a java class like "PropertyManager". When we build the "debug" variant, all the property files from that "debug" folder are taken. – 最白目 Dec 01 '16 at 07:19