Build variants in Gradle for a Library Project in Android
Asked Answered
M

6

22

I am trying to configure with Gradle a project which contains some external libraries. With Gradle I can setup different Environmental Configuration (with a class inside a config file) for the main application using the Build Variants so I can execute code according to this variables.

The problem is that how I can do the same for a library project? I created this library for this project and I would like to setup different Build Variants for different scenarios.

As an example: In the Library, when running in debug mode, then print all the logs so I can see them while developing. In release mode dont.

Structure of the files:

src ----- > debug -> java -> config -> PlayerEnvConfig
            main -> com.mypackagename -> etc...
            release -> java -> config -> PlayerEnvConfig

Code in debug: package config;

/**
 * Environment configuration for Release
*/
public final class PlayerEnvConfig {
    public static final boolean USE_REPORTING = true;
    public static final boolean USE_ANALYTICS = true;
    public static final boolean USE_LOGGING = false;
    public static final boolean USE_DEBUG_LOGGING = false;
    public static final boolean USE_DEBUGING = false;
}

Code in release:

package config;

/**
 * Environment configuration for Release
*/
public final class PlayerEnvConfig {
    public static final boolean USE_REPORTING = true;
    public static final boolean USE_ANALYTICS = true;
    public static final boolean USE_LOGGING = false;
    public static final boolean USE_DEBUG_LOGGING = false;
    public static final boolean USE_DEBUGING = false;
}

The problem is that for the main project I can use this Build types to configure differently the application for different scenarios, but how can I do the same for the Library Project?

Because at the moment from what I read in http://tools.android.com/tech-docs/new-build-system/user-guide the library only will use the debug mode while testing.

Any ideas?

Thanks!

Makedamakefast answered 3/7, 2013 at 15:15 Comment(0)
T
16

It's a @bifmadei answer from google code issue and it helps for me:

Obsolete 1: Try setting this in the dependency project

android {
    publishNonDefault true
    ...
}

Obsolete 2: Starting from gradle 4.10.1 publishNonDefault is true by default. So just use the recommendation below:

Include this in the project that uses it

dependencies {
    releaseCompile project(path: ':theotherproject', configuration: 'release')
    debugCompile project(path: ':theotherproject', configuration: 'debug')
}

Taken from here: https://code.google.com/p/android/issues/detail?id=66805

Note that with implementation instruction separated releaseCompile and debugCompile become obsolete: https://mcmap.net/q/331300/-gradle-error-configuration-declares-dependency-which-is-not-declared

Update:

Approach from the previous step still can be used with a custom build configuration:

implementation project(path: ':theotherproject', configuration: 'staging')
Toothwort answered 25/9, 2014 at 11:24 Comment(2)
This worked very well indeed for me. Note that you can use custom buildTypes too. I have a buildType for simulator so I add simulatorCompile to my gradle file. If you have it setup correctly, when you switch Build Variant via AndroidStudio 1.2.2, the correct Build Variant will ripple through the dependent modules.Tincal
Unfortunately releaseCompile doesn't work in gradle-experimental.Berrios
B
12

Not sure what's wrong with your configuration but about your need, I would do it differently.

In the gradle build file you can use the buildConfig keyword to add a specific line to the BuildConfig.java generated class.

So you could add do something like that in your build.gradle :

    release {
        buildConfig "public static final String USE_REPORTING = true;"
    }
    debug {

        buildConfig "public static final String USE_REPORTING = false;"
    }

And so have only one PlayerEnvConfig with

public static final boolean USE_REPORTING = BuildConfig.USE_REPORTING;

Or even no more PlayerEnvConfig and use directly the BuildConfig class.


EDIT Since an update, the syntax has changed :

buildConfigField "<type>", "<name>", "<value>"
Beeline answered 4/7, 2013 at 12:3 Comment(1)
Thanks tbruyelle I tried it (much more intelligent this way btw) but the problem still remains. The issue relays in the fact that when building to run the application the libraries are always build in release mode (even though I selected debug mode in Android Studio). In the link I posted before explains this. But does it exists any workaround? "The debug type is used by the test application. The release type is used by projects using the library."Makedamakefast
R
4

This is documented in https://code.google.com/p/android/issues/detail?id=52962 . As you've found out, the build type isn't propagated to library projects, and there isn't a good workaround. If you have control over the code of the library project, you could make the debug status a mutable global variable, and set it from your main application on startup. It's a bit of a hack, and it has the disadvantage that the compiler can't optimize the unused code paths away from release builds, but unless something unusual is going on it should work.

Raincoat answered 13/11, 2013 at 22:36 Comment(0)
L
3

UPDATE - since the time of this posting, there have been much progress in the gradle build process, thus this answer might not be the recommended best practice and new changes might even brake it. Use your own discretion.

I believe that there is a bit of confusion over the whole project structure and configuration. Let's say that you have the following build.gradle configuration

sourceSets {

    main {
        manifest.srcFile 'src/main/AndroidManifest.xml'
        java.srcDirs = ['src/main/java']
        //resources.srcDirs = ['src/main']
        //aidl.srcDirs = ['src/main']
        //renderscript.srcDirs = ['src/main']
        res.srcDirs = ['src/main/res']
        assets.srcDirs = ['src/main/assets']
    }

    debug.setRoot('build-types/debug')
    release.setRoot('build-types/release')
}

Your project folder structure should be as follow

project_root
   -src
      -main
         -java
            -com
               -example
   -build-types
      -debug
         -java
            -com
               -example
                  -FooBar.java
      -release
         -java
            -com
               -example
                  -FooBar.java

FooBar.java must not be in the prooject_root/src/main/java/com/example. It must be in debug and release folder which is to reside outside src folder but inside build-types folder. That is configured by setRoot('build-types/*****') method. A lot of people get confused from seeing 'debug/java' and 'main/java', where the later is referenced in a manner 'src/main/java' from presentation and end up putting 'debug/java' in src, the wrong folder. I hope this help.

For more complex environment involving other libraries, you can check out my answer here https://mcmap.net/q/412339/-gradle-android-build-for-different-processor-architectures

Leandroleaning answered 12/11, 2013 at 22:56 Comment(0)
C
0

This is not possible with library projects as you yourself mentioned.

You could just change the library project to an application project. That seems to work fine (at least in theory, I haven't tested it myself).

The other way would be to override that class in your application project. Your application project class will be chosen when merging takes place and you'll have those values available.

Cheyenne answered 31/8, 2013 at 4:34 Comment(2)
As far as I know, application modules can't have dependencies on other application modules. Also had a reference for that, just can't find it anymore.Copro
Yes. I found that out as well. It was possible with older build system I believe was not propagated to gradle.Cheyenne
S
0

As Scott points out, this is a known shortcoming of Gradle. As a workaround, you can use this method, which uses reflection to get the field value from the app (not the library):

/**
 * Gets a field from the project's BuildConfig. This is useful when, for example, flavors
 * are used at the project level to set custom fields.
 * @param context       Used to find the correct file
 * @param fieldName     The name of the field-to-access
 * @return              The value of the field, or {@code null} if the field is not found.
 */
public static Object getBuildConfigValue(Context context, String fieldName) {
    try {
        Class<?> clazz = Class.forName(context.getPackageName() + ".BuildConfig");
        Field field = clazz.getField(fieldName);
        return field.get(null);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return null;
}

To get the DEBUG field, for example, just call this from your Activity:

boolean debug = (Boolean) getBuildConfigValue(this, "DEBUG");

I have also shared this solution on the AOSP Issue Tracker.

Stendhal answered 27/8, 2014 at 1:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.