Is it possible to declare a variable in Gradle usable in Java ? Basically I would like to declare some vars in the build.gradle and then getting it (obviously) at build time. Just like a pre-processor macros in C/C++...
An example of declaration would be something like that ... :
android {
debug {
A_VAR_RETRIEVABLE_IN_JAVA = 42
}
release {
A_VAR_RETRIEVABLE_IN_JAVA = 42+52
}
}
Is there a way to do something like that ?
This question is related to
java
android
gradle
android-gradle-plugin
How can you insert String result of function into buildConfigField
Here's an example of build date in human-readable format set:
def getDate() {
return new SimpleDateFormat("dd MMMM yyyy", new Locale("ru")).format(new Date())
}
def buildDate = getDate()
defaultConfig {
buildConfigField "String", "BUILD_DATE", "\"$buildDate\""
}
You can create build config field overridable via system environment variables during build:
Fallback is used while developing, but you can override the variable when you run the build on Jenkins or another tool.
In your app build.gradle:
buildTypes {
def serverUrl = '\"' + (System.getenv("SERVER_URL")?: "http://default.fallback.url.com")+'\"'
debug{
buildConfigField "String", "SERVER_URL", serverUrl
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField "String", "SERVER_URL", serverUrl
}
}
The variable will be available as BuildConfig.SERVER_URL
.
None of the above answers gave me any guidelines so I had to spend two hours learning about Groovy Methods.
I wanted be able to go against a production, sandbox and local environment. Because I'm lazy, I only wanted to change the URL at one place. Here is what I came up with:
flavorDimensions 'environment'
productFlavors {
production {
def SERVER_HOST = "evil-company.com"
buildConfigField 'String', 'API_HOST', "\"${SERVER_HOST}\""
buildConfigField 'String', 'API_URL', "\"https://${SERVER_HOST}/api/v1/\""
buildConfigField 'String', 'WEB_URL', "\"https://${SERVER_HOST}/\""
dimension 'environment'
}
rickard {
def LOCAL_HOST = "192.168.1.107"
buildConfigField 'String', 'API_HOST', "\"${LOCAL_HOST}\""
buildConfigField 'String', 'API_URL', "\"https://${LOCAL_HOST}/api/v1/\""
buildConfigField 'String', 'WEB_URL', "\"https://${LOCAL_HOST}/\""
applicationIdSuffix ".dev"
}
}
Alternative syntax, because you can only use ${variable}
with double quotes in Groovy Methods.
rickard {
def LOCAL_HOST = "192.168.1.107"
buildConfigField 'String', 'API_HOST', '"' + LOCAL_HOST + '"'
buildConfigField 'String', 'API_URL', '"https://' + LOCAL_HOST + '/api/v1/"'
buildConfigField 'String', 'WEB_URL', '"https://' + LOCAL_HOST + '"'
applicationIdSuffix ".dev"
}
What was hard for me to grasp was that strings needs to be declared as strings surrounded by quotes. Because of that restriction, I couldn't use reference API_HOST
directly, which was what I wanted to do in the first place.
gradle.properties
AppKey="XXXX-XXXX"
build.gradle
buildTypes {
//...
buildTypes.each {
it.buildConfigField 'String', 'APP_KEY_1', AppKey
it.resValue 'string', 'APP_KEY_2', AppKey
}
}
Usage in java code
Log.d("UserActivity", "onCreate, APP_KEY: " + getString(R.string.APP_KEY_2));
BuildConfig.APP_KEY_1
Usage in xml code
<data android:scheme="@string/APP_KEY_2" />
I'm using this code and working very fine.
def baseUrl = '\"http://patelwala.com/myapi/"'
def googleServerKey = '\"87171841097-opu71rk2ps35ibv96ud57g3ktto6ioio.apps.googleusercontent.com"'
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField 'String', 'BASE_URL', baseUrl
buildConfigField 'String', 'web_client_id', googleServerKey
}
releasedebug {
initWith debug
buildConfigField 'String', 'BASE_URL', baseUrl
buildConfigField 'String', 'web_client_id' ,googleServerKey
}
debug {
buildConfigField 'String', 'BASE_URL', baseUrl
buildConfigField 'String', 'web_client_id', googleServerKey
}
}
}
}
rciovati's answer is entirely correct I just wanted to add one more tidbit that you can also create variables for every build type within the default config portion of your build.gradle. This would look like this:
android {
defaultConfig {
buildConfigField "String", "APP_NAME", "\"APP_NAME\""
}
}
This will allow you to have access to through
BuildConfig.App_NAME
Just wanted to make a note of this scenario as well if you want a common config.
I'm using
buildTypes.each {
it.buildConfigField 'String', 'GoogleMapsApiKey', "\"$System.env.GoogleMapsApiKey\""
}
Its based on Dennis's answer but grabs its from an environment variable.
https://stackoverflow.com/a/17201265/12021422 Answer by @rciovati works
I spent 30 minutes trying to figure out why the new property variables aren't accessible.
Example using system properties, set in build.gradle, read from Java application (following up from question in comments):
Basically, using the test
task in build.gradle
, with test task method systemProperty
setting a system property that's passed at runtime:
apply plugin: 'java'
group = 'example'
version = '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
// mavenLocal()
// maven { url 'http://localhost/nexus/content/groups/public'; }
}
dependencies {
testCompile 'junit:junit:4.8.2'
compile 'ch.qos.logback:logback-classic:1.1.2'
}
test {
logger.info '==test=='
systemProperty 'MY-VAR1', 'VALUE-TEST'
}
And here's the rest of the sample code (which you could probably infer, but is included here anyway): it gets a system property MY-VAR1
, expected at run-time to be set to VALUE-TEST
:
package example;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
static final Logger log=LoggerFactory.getLogger(HelloWorld.class);
public static void main(String args[]) {
log.info("entering main...");
final String val = System.getProperty("MY-VAR1", "UNSET (MAIN)");
System.out.println("(main.out) hello, world: " + val);
log.info("main.log) MY-VAR1=" + val);
}
}
Testcase: if MY-VAR
is unset, the test should fail:
package example;
...
public class HelloWorldTest {
static final Logger log=LoggerFactory.getLogger(HelloWorldTest.class);
@Test public void testEnv() {
HelloWorld.main(new String[]{});
final String val = System.getProperty("MY-VAR1", "UNSET (TEST)");
System.out.println("(test.out) var1=" + val);
log.info("(test.log) MY-VAR1=" + val);
assertEquals("env MY-VAR1 set.", "VALUE-TEST", val);
}
}
Run (note: test is passing):
$ gradle cleanTest test
:cleanTest
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test
BUILD SUCCESSFUL
I've found that the tricky part is actually getting the output from gradle... So, logging is configured here (slf4j+logback), and the log file shows the results (alternatively, run gradle --info cleanTest test
; there are also properties that get stdout to the console, but, you know, why):
$ cat app.log
INFO Test worker example.HelloWorld - entering main...
INFO Test worker example.HelloWorld - main.log) MY-VAR1=VALUE-TEST
INFO Test worker example.HelloWorldTest - (test.log) MY-VAR1=VALUE-TEST
If you comment out "systemProperty...
" (which, btw, only works in a test
task), then:
example.HelloWorldTest > testEnv FAILED
org.junit.ComparisonFailure at HelloWorldTest.java:14
For completeness, here is the logback config (src/test/resources/logback-test.xml
):
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>app.log</file>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d %p %t %c - %m%n</pattern>
</layout>
</appender>
<root level="info">
<appender-ref ref="FILE"/>
</root>
</configuration>
Files:
build.gradle
src/main/java/example/HelloWorld.java
src/test/java/example/HelloWorldTest.java
src/test/resources/logback-test.xml
Source: Stackoverflow.com