Gradle and loading properties (Problem in AndroidStudio)

3,257 views
Skip to first unread message

Rainer Burgstaller

unread,
Jun 19, 2013, 1:25:15 AM6/19/13
to adt...@googlegroups.com
Hi

I am trying to figure out how to externalize signing configs into local.properties (or some other file). In principle on the commandline this works just fine using the following code


def loadProperties(String sourceFileName) {
    def config = new Properties()
    def propFile = new File(sourceFileName)
    System.err.println("Loading property file: " + propFile.absolutePath)
    if (propFile.canRead()) {
        config.load(new FileInputStream(propFile))
        for (Map.Entry property in config) {
            System.out.println("setting " + property.key)
            project.ext[property.key] = property.value;
        }
    }
}


loadProperties("local.properties")

android {
    compileSdkVersion 16
    buildToolsVersion "17"

    defaultConfig {
        minSdkVersion 9
        targetSdkVersion 17
        versionCode 2
        versionName getVersionName()
    }

    signingConfigs {
        mySigning {
            storeFile file("/Users/user/my-keystore.ks")
            storePassword project.ext.get("key.store.password")
            keyAlias "keyalias"
            keyPassword project.ext.get("key.alias.password")
        }
    }

However, when I run this in Android Studio it seems to pick up the local.properties from some other location (one that is missing the signing configs). So I cannot import the gradle project anew in AndroidStudio nor can I successfully refresh the file. On the commandline everything works fine.

So I have 3 questions

1. is this the way its supposed to work? I found it quite counterintuitive to have to implement something so basic as loading properties from a properties file myself
2. is there a way to see the output of the gradle task that is done by AndroidStudio
3. Which pwd is AndroidStudio using when its importing this build.gradle 

I should also mention that I have a multi-project configuration and the local.properties with the signing info is both in the parent dir as well as in my app dir.

any help would be appreciated.
- Rainer

Xavier Ducrohet

unread,
Jun 19, 2013, 1:38:36 AM6/19/13
to adt...@googlegroups.com
1. I don't think we use local.properties from the IDE since we use the SDK bundled with it, though we need to figure this out better.
The question of the SDK location is an important one. If you have a local.properties that points to an SDK that is not the one bundled with Studio, which one should we use from within Studio?

For build repeatability, I think we should use the one from local.properties but that makes things more complicated for the IDE to load data from the SDK when dealing with two projects that use a different SDK. We're going to have to decide on the proper strategy before 1.0.

That said since you load the local.properties file manually there should be no difference whether we use local.properties or not. However your code is dependent on what the working dir is.
I would change the code to read

def propFile = file(sourceFileName)

in this case file() is a method of Project that converts a path to a File. If the path is not absolute it'll find the file relative to the root of the project, so this won't change based on the working dir.

There might be a way to load a prop file more easily from Gradle, I'll have to look into it.

2/3. Not sure actually.

In general I want to improve things regarding signing. I want to find a better way to handle passwords without having to do a lot of the boiler plate you just did. I also want to be able to inject the password from the IDE (to allow us to have a signing wizard in Studio, like we have one in ADT).

We have good flexible low level API in the current DSL. Now I want to build a layer on top of it that will help build a good signing strategy for both CI and IDE. I'm just not sure what this should look like.



- Rainer

--
You received this message because you are subscribed to the Google Groups "adt-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to adt-dev+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Xavier Ducrohet
Android SDK Tech Lead
Google Inc.
http://developer.android.com | http://tools.android.com

Please do not send me questions directly. Thanks!

Rainer Burgstaller

unread,
Jun 19, 2013, 2:00:40 AM6/19/13
to adt...@googlegroups.com
Changed my loadProperties method as you described and now it seems to work. For some bizarre reason the pwd within AndroidStudio is neither the project root nor the app root.

So in case anyone else wonders on how to properly load properties, I leave this here for the next generation :)


def loadProperties(String sourceFileName) {
    def config = new Properties()
    def propFile = file(sourceFileName)
    System.err.println("Loading property file: " + propFile.absolutePath)
    if (propFile.canRead()) {
        config.load(new FileInputStream(propFile))
        for (Map.Entry property in config) {
            System.out.println("setting " + property.key)
            project.ext[property.key] = property.value;
        }
    }
}

Regarding local.properties, I just stuck it there since I already had it and it was gitignored, obviously I could have used any other property file here. However, as you found, it is quite an important requirement to be able to extract passwords out of the main build.gradle file. I obviously wouldnt want to check this in.

In addition to the use case from within IDE, I would also like to be able to set/override these properties via commandline switches to gradle, in order to be able to build on a CI environment (such as jenkins)

cheers
- Rainer

Xavier Ducrohet

unread,
Jun 19, 2013, 2:18:12 AM6/19/13
to adt...@googlegroups.com
yeah I don't know what the working dir is from the IDE.

One thing that we could look into is reading env vars and see if we find

signingConfigs.<signingConfig name>.<property name>

so for example if you were to run the following commands with your script:

$ export signingConfigs.mySigning.storePassword = ...
$ export signingConfigs.mySigning.keyPassword = ...
$ ./gradlew assemble

this would automatically set these values in your signingConfigs.mySigning object and then run the build.

This advantage is that this is based on the model itself, so from the IDE we can load the list of SigningConfig object, and offer through a dynamic UI the ability to set any of them.

I think you could also run it in a single command with

./gradlew -DsigningConfigs.mySigning.storePassword=... -DsigningConfigs.mySigning.keyPassword=... assemble


Burgstaller Rainer

unread,
Jun 19, 2013, 2:46:11 AM6/19/13
to adt...@googlegroups.com, adt...@googlegroups.com
I thought about that but for normal use this is somewhat of a hassle. I enjoyed how ant handled this with its override approach of properties. 
I would wish that the gradle system would offer something comparable. 
It's just much more convenient to just type gradle build rather than having to set up env vars first. Also note that I work on multiple projects for multiple clients at the same time therefore setting the signing details in my bashrc doesn't really work. 
Of course I could launch gradle from a shell script or from ant. But this would be a very roundabout way of doing things. 
I would suggest to provide some similar functionality where you can offload things into properties and then allow overrides via environment switches. I guess I could pretty easily extend my mini sample to allow for something like that. I was just hoping that the existing build system would already support this. 

Will google around a bit more and see whether gradle has something like this already. After all ant and maven do. 

Thanks for your fast response and support. I really appreciate it. 

Cheers
Rainer 


Sent on the go
You received this message because you are subscribed to a topic in the Google Groups "adt-dev" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/adt-dev/1U1evCbcLto/unsubscribe.
To unsubscribe from this group and all its topics, send an email to adt-dev+u...@googlegroups.com.

Rainer Burgstaller

unread,
Jun 19, 2013, 4:46:36 AM6/19/13
to adt...@googlegroups.com

I think you could also run it in a single command with

./gradlew -DsigningConfigs.mySigning.storePassword=... -DsigningConfigs.mySigning.keyPassword=... assemble



I tried this, but it didnt work. 

I tried also putting  these in my gradle.properties

signingConfigs.releaseSigning.storeFile=/tmp/android-keystore.ks
android.signingConfigs.releaseSigning.storeFile=/tmp/android-keystore.ks

which yields

10:25:02.616 [ERROR] [org.gradle.BuildExceptionReporter] Caused by: java.lang.NullPointerException: Cannot invoke method exists() on null object
10:25:02.616 [ERROR] [org.gradle.BuildExceptionReporter] at java_io_File$exists.call(Unknown Source)
10:25:02.616 [ERROR] [org.gradle.BuildExceptionReporter] at com.android.build.gradle.internal.tasks.ValidateSigningTask.validate(ValidateSigningTask.groovy:57)
10:25:02.616 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.api.internal.BeanDynamicObject$MetaClassAdapter.invokeMethod(BeanDynamicObject.java:216)
10:25:02.617 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.api.internal.BeanDynamicObject.invokeMethod(BeanDynamicObject.java:122)
10:25:02.617 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.api.internal.CompositeDynamicObject.invokeMethod(CompositeDynamicObject.java:147)
10:25:02.617 [ERROR] [org.gradle.BuildExceptionReporter] at com.android.build.gradle.internal.tasks.ValidateSigningTask_Decorated.invokeMethod(Unknown Source)
10:25:02.617 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.util.ReflectionUtil.invoke(ReflectionUtil.groovy:23)
10:25:02.617 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.doExecute(AnnotationProcessingTaskFactory.java:217)
10:25:02.617 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:210)
10:25:02.617 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:199)
10:25:02.618 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:526)
10:25:02.618 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:509)
10:25:02.618 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
10:25:02.618 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)


In addition, I tried putting the value in some other property in gradle.properties such as

signStoreFile=/tmp/android-keystore.ks

and then try to refer to it via the build.gradle like this


android {
    compileSdkVersion 16
    buildToolsVersion "17"


    signingConfigs {
        releaseSigning {
            storeFile project.signStoreFile
            storePassword project.signStorePassword
            keyAlias project.signKeyAlias
            keyPassword project.signKeyPassword
        }
    }

which produces

* Where:
Build file '/Users/rainer/Documents/workspaces/privat/PtpWorkspace/OfParent/app/build.gradle' line: 45

* What went wrong:
A problem occurred evaluating project ':app'.
> Could not find method storeFile() for arguments [/tmp/android-keystore.ks] on SigningConfigDsl_Decorated{name=releaseSigning, storeFile=null, storePassword=null, keyAlias=null, keyPassword=null, storeType=null}.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

I finally managed to get it "quasi" working via the following

android {
    signingConfigs {
        releaseSigning {
            storeFile file(project.signStoreFile)
            storePassword project.signStorePassword
            keyAlias project.signKeyAlias
            keyPassword project.signKeyPassword
        }
    }
...

However, I still cannot override it via 

gradle build -DsignStorePassword=hello

I am still new to gradle so I guess there would be a way to achieve this.

cheers
- Rainer
Reply all
Reply to author
Forward
0 new messages