Android gradle build: how to set global variables - gradle

How can I set a global variable that can be accessed from build.gradle and tasks?

To set a global variable
project.ext.set("variableName", value)
To access it from anywhere in the project:
project.variableName
For instance:
project.ext.set("newVersionName", versionString)
and then...
println project.newVersionName
For more information see: http://www.gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html
EDIT:
As commented by Dmitry, In new versions you can use the following shorthand:
project.ext.variableName = value

The answer from Guy is excellent. I just want to add the practical code.
Example:
Put something like this in the Project build.gradle:
project.ext {
minSdkVersion = 21
targetSdkVersion = 23
}
And put something like this in the Module build.gradle to access it:
defaultConfig {
minSdkVersion.apiLevel project.minSdkVersion
targetSdkVersion.apiLevel project.targetSdkVersion
}

You can also do this. Let's say you want to add appcompat with the version 25.3.1, you can add a variable version_name in your project level build.gradle.
buildscript{
ext.version_name = '25.3.1'
}
Now you can add this to your application level build gradle and avoid any conflicts.
compile "com.android.support:appcompat-v7:$version_name"
compile "com.android.support:recyclerview-v7:$version_name"
compile "com.android.support:design:$version_name"

You cant create a Gradle file in the project's root directory and put all variables there, like this:
lib-versions.gradle
ext {
kt_core = '1.6.0'
app_compat = '1.3.1'
material = '1.4.0'
constraintlayout = '2.1.1'
nav_version = '2.3.5'
junit = '4.13.2'
junit_ext = '1.1.3'
escpresso = '3.4.0'
}
Then in the project build.gradle file on the bottom you should apply the Gradle file like this:
buildscript {
...
}
task clean(type: Delete) {
delete rootProject.buildDir
}
apply from: 'lib-versions.gradle' //apply it like this
Then you are able to use the variables in any module-level build Gradle file (app/build.gradle) like this:
implementation "androidx.core:core-ktx:$rootProject.ext.kt_core"
implementation "androidx.appcompat:appcompat:$rootProject.ext.app_compat"
implementation "com.google.android.material:material:$rootProject.ext.material"
implementation "androidx.constraintlayout:constraintlayout:$rootProject.ext.constraintlayout"
...

Additional, for dynamic global variables you can define global functions in the master build.gradle file:
First, define your function, for example for git branch:
def getGitBranch = { ->
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--abbrev-ref', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim()
}
In allProjects section set the variable:
allprojects {
repositories {
google()
jcenter()
}
project.ext {
gitBranch="\"${getGitBranch()}\""
}
}
In your build.gradle files of your sub projects or android modules, get this variable like this:
android {
compileSdkVersion project.mCompileSdkVersion.toInteger()
defaultConfig {
minSdkVersion project.mMinSdkVersion.toInteger()
...
buildConfigField "String", "GitBranch", project.gitBranch
}
...
}
Finally, you can use it in your code like this:
public static String getGitBranch() {
return BuildConfig.GitBranch;
}

This is for Kotlin DSL (build.gradle.kts).
Root (top-level) build file:
val myVariable by extra("watermelon")
// Alternative notations:
// extra.set("myVariable", "watermelon")
// extra["myVariable"] = "watermelon"
Extra properties on a project object are visible from its subprojects. Note that extra. is equivalent to project.extra.; in other words the project object is implicit.
A sub-project build file:
val myVariable: String by rootProject.extra
// Alternative notations:
// val myVariable: String by rootProject
// val myVariable: String = rootProject.extra["myVariable"] as String
// val myVariable: String = rootProject.extra.get("myVariable") as String
Note that when using Kotlin delegation (by keyword) the name of the variables should be the same in both build files.
See Gradle Docs: Extra properties for more information.

Related

Access dot property stored in gradle.properties within settings.gradle

I have a Gradle 6.0.1 project. The build.gradle (excerpt) looks like:
plugins {
id "application"
id "com.github.edeandrea.xjc-generation"
id "eclipse"
id "idea"
id "java"
id "org.springframework.boot"
}
...
dependencies {
implementation platform("org.springframework.boot:spring-boot-dependencies:${property("spring-boot.version")}")
// ...more stuff here
}
// ...more stuff here
I'm managing all the plugin versions in settings.gradle:
pluginManagement {
plugins {
id "application"
id "com.github.edeandrea.xjc-generation" version "1.0"
id "eclipse"
id "idea"
id "java"
id "org.springframework.boot" version "${spring-boot.version}"
}
}
rootProject.name = "spring-core"
...and I usually put the artifact versions in gradle.properties:
#
# Dependency Versions
oracle.version = 18.3.0.0
spring-boot.version = 2.2.1.RELEASE
#
# Gradle Settings
org.gradle.configureondemand = false
org.gradle.daemon = false
#
# System Settings
systemProp.file.encoding = UTF-8
systemProp.sun.jnu.encoding = UTF-8
Now the problem is I can't read dot-properties in settings.gradle (from gradle.properties) in the same way I do it inside build.gradle — I already tried using ${property("spring-boot.version")}.
Is there any way to achieve that? I can easily change the key to something like springBootVersion and it works, but I wonder if there is a way to have in the way I currently have it: spring-boot.version.
use getProperty("spring-boot.version")
simple gradle project with additional variants
task test{
doLast {
//success
println project.property('aaa.bbb.ccc')
println project.'aaa.bbb.ccc'
println getProperty('aaa.bbb.ccc')
//failure: Could not get unknown property 'aaa.bbb.ccc' for task ':test' of type org.gradle.api.DefaultTask
println property('aaa.bbb.ccc')
}
}
gradle.properties
aaa.bbb.ccc=12345
property('aaa.bbb.ccc') fails because it tries to get property on current object (task) but aaa.bbb.ccc defined for project
however project.property('aaa.bbb.ccc') succeeds because it should
project.'aaa.bbb.ccc' is the same as project.getProperty('aaa.bbb.ccc') in groovy
project.getProperty('aaa.bbb.ccc') works because of groovy basic object GroovyObject (IHMO)
and getProperty(name) without prefix actualy located in org.gradle.groovy.scripts.BasicScript and not really documented...
For me the solution from #daggett doesn't work. But I was able to access a property defined inside the ~/.gradle/gradle.properties from the settings.gradle this way:
settings.ext.find('MY_PROPERTY')
To provide some context, I'm using this to read the credentials for the Gradle Build Cache Node remote server:
buildCache {
boolean isCiServer = System.getenv().containsKey("CI")
remote(HttpBuildCache) {
enabled = settings.ext.find('GRADLE_BUILD_CACHE_NODE_PWD') != null
url = 'https://myserver:443/cache/'
allowUntrustedServer = true
push = isCiServer
credentials {
username = settings.ext.find('GRADLE_BUILD_CACHE_NODE_USR')
password = settings.ext.find('GRADLE_BUILD_CACHE_NODE_PWD')
}
}
}
EDIT
Another solution that does not require the use of ext:
username = settings.hasProperty('GRADLE_BUILD_CACHE_NODE_USR') ? settings.GRADLE_BUILD_CACHE_NODE_USR : null

setting properties for Gradle subproject

I have a multi-project Gradle build. In each subproject I have a properties.gradle file like the following:
def usefulMethod() {
return 'foo'
}
ext {
foo = 'bar'
usefulMethod = this.&usefulMethod
}
And then I import it into the subproject build.gradle using apply from: './properties.gradle'.
However, if two subprojects import a variable with the same name, I get this error:
Cannot add extension with name 'foo', as there is an extension already registered with that name.
It seems that adding to ext affects the entire project instead of just the subproject like I wanted. What is the correct way to import properties and variables from an external file in a subproject without leaking them into the entire project build?
The plain ext is the extension for ENTIRE project, the root project and all subprojects. To avoid polluting the root namespace whenever you include a file via apply from..., you should instead use project.ext. project refers to the current project or subproject being built. For example, the below file could be apply from'd to add a downloadFile function to the current project:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'de.undercouch:gradle-download-task:3.4.3'
}
}
project.ext {
apply plugin: de.undercouch.gradle.tasks.download.DownloadTaskPlugin
downloadFile = { String url, File destination ->
if (destination.exists()) {
println "Skipping download because ${destination} already exists"
} else {
download {
src url
dest destination
}
}
}
}

I got an error using this build.grafle file and don't know how to fix it

Here's the Error:
FAILURE: Build failed with an exception.
Where: Build file '/home/wieland/GitGradlePackaging/build.gradle' line: 22
What went wrong: A problem occurred evaluating root project 'GitGradlePackaging'.
Could not get unknown property 'org' for object of type org.gradle.api.internal.initialization.DefaultScriptHandler.
And Here's my build.gradle File:
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java project to get you started.
* For more details take a look at the Java Quickstart chapter in the Gradle
* user guide available at https://docs.gradle.org/4.6/userguide/tutorial_java_projects.html
*/
//From example: http://mrhaki.blogspot.co.at/2015/04/gradle-goodness-use-git-commit-id-in.html
buildscript {
repositories {
jcenter()
}
dependencies {
//Add dependencies for build script, so we can access Git from our build script
classpath 'org.ajoberstar:grgit:1.1.0'
}
def git = org.ajoberstar.grgit.Grgit.open(file('.'))
//To save Githash
def githash = git.head().abbreviatedId
}
plugins {
// Apply the java plugin to add support for Java
id 'java'
// Apply the application plugin to add support for building an application
id 'application'
// Apply the groovy plugin to also add support for Groovy (needed for Spock)
id 'groovy'
id 'distribution'
}
// Set version
project.version = mainProjectVersion + " - " + githash
project.ext.set("wholeVersion", "$project.version - $githash")
project.ext.set("buildtimestamp", "2000-01-01 00:00")
def versionfilename = "versioninfo.txt"
def GROUP_DEBUG = 'Debug'
// Task to print project infos
task debugInitialSettings {
group = GROUP_DEBUG
doLast {
println 'Version: ' + project.wholeVersion
println 'Timestamp: ' + project.buildtimestamp
println 'Filename: ' + project.name
}
}
// To add the githash to zip
task renameZip {
doLast {
new File ("$buildDir/distributions/$project.name-${project.version}.zip")
.renameTo ("$buildDir/distributions/$project.name-${project.wholeVersion}.zip")
}
}
distZip.finalizedBy renameZip
// To add the githash to tar
task renameTar{
doLast {
new File ("$buildDir/distributions/$project.name-${project.version}.tar")
.renameTo ("$buildDir/distributions/$project.name-${project.wholeVersion}.tar")
}
}
distTar.finalizedBy renameTar
// Define the main class for the application
mainClassName = 'App'
dependencies {
// This dependency is found on compile classpath of this component and consumers.
compile 'com.google.guava:guava:23.0'
// Use the latest Groovy version for Spock testing
testCompile 'org.codehaus.groovy:groovy-all:2.4.13'
// Use the awesome Spock testing and specification framework even with Java
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile 'junit:junit:4.12'
}
// In this section you declare where to find the dependencies of your project
repositories {
// Use jcenter for resolving your dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
}
//To generate Testreports as HTML
test {
reports {
junitXml.enabled = false
html.enabled = true
}
}
distributions {
main {
contents {
from { 'build/docs' }
into ('reports') {
from 'build/reports'
}
}
}
}
//To make sure that test and javadoc ran before zip and tar
distTar.dependsOn test
distZip.dependsOn test
distTar.dependsOn javadoc
distZip.dependsOn javadoc
Please keep in mind I have not much knowledge about gradle as I'm just starting to learn it!
Thanks in advance :)
You have to move the githash definition outside the buildscript block
buildscript {
repositories {
jcenter()
}
dependencies {
//Add dependencies for build script, so we can access Git from our build script
classpath 'org.ajoberstar:grgit:1.1.0'
}
}
def git = org.ajoberstar.grgit.Grgit.open(file('.'))
//To save Githash
def githash = git.head().abbreviatedId
The reason is that when the buildscript block is evaluated line by line, its dependencies are not yet loaded. When the rest of the script is evaluated, the dependencies of the buildscript block have already been loaded. This is actually the reason for the buildscript block existence: to be run before the rest of the build and prepare the setup.

ext in buildscript can not be recognised by Gradle Kotlin DSL

In these days, I am trying to write some codes to experience the Spring reactive features and kotlin extension in Spring 5, and I also prepared a gradle Kotlin DSL build.gradle.kt to configure the gradle build.
The build.gradle.kt is converted from Spring Boot template codes generated by http://start.spring.io.
But the ext in the buildscript can not be detected by Gradle.
buildscript {
ext { }
}
The ext will cause Gradle build error.
To make the variables in classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") and compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlinVersion") work, I added the variables in the hard way.
val kotlinVersion = "1.1.4"
val springBootVersion = "2.0.0.M3"
But I have to declare them in global top location and duplicate them in the buildscript.
Code: https://github.com/hantsy/spring-reactive-sample/blob/master/kotlin-gradle/build.gradle.kts
Is there a graceful approach to make ext work?
Update: There are some ugly approaches:
From Gradle Kotlin DSL example, https://github.com/gradle/kotlin-dsl/tree/master/samples/project-properties, declares the properties in gradel.properties.
kotlinVersion = 1.1.4
springBootVersion = 2.0.0.M3
And use it in build.gradle.kts.
buildScript{
val kotlinVersion by project
}
val kotlinVersion by project //another declare out of buildscript block.
Similar with above declare them in buildScript block:
buildScript{
extra["kotlinVersion"] = "1.1.4"
extra["springBootVersion"] = "2.0.0.M3"
val kotlinVersion: String by extra
}
val kotlinVersion: String by extra//another declare out of buildscript block.
How can I avoid the duplication of val kotlinVersion: String by extra?
With Kotlin DSL ext has been changed to extra and it can be used under buildscript.
Eg :-
buildscript {
// Define versions in a single place
extra.apply{
set("minSdkVersion", 26)
set("targetSdkVersion", 27)
}
}
It is possible to use constants defined in .kt file in .gradle.kts files.
create buildSrc folder in root folder of your project
create buildSrc/build.gradle.kts file with the following content
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
create file buildSrc/src/main/kotlin/Constants.kt with the following content
object Constants {
const val kotlinVersion = "1.3.70"
const val targetSdkVersion = 28
}
Synchronize. Now you may reference created constants in various .gradle.kts files like this
...
classpath(kotlin("gradle-plugin", version = Constants.kotlinVersion))
...
...
targetSdkVersion(Constants.targetSdkVersion)
...
What is working for me is using ext in allprojects instead of buildscript, so in your top-level build.gradle.kts
allprojects {
ext {
set("supportLibraryVersion", "26.0.1")
}
}
then you can use it in build.gradle.kts files in modules like this:
val supportLibraryVersion = ext.get("supportLibraryVersion") as String
None of these answers felt clear to me.
So here's my explanation:
/build.gradle.kts:
buildscript {
extra.apply {
set("compose_version", "1.0.3")
}
...
}
/app/build.gradle.kts:
val composeVersion = rootProject.extra["compose_version"]
implementation("androidx.compose.ui:ui:$composeVersion")
implementation("androidx.compose.material:material:$composeVersion")
There is a new possibility with Kotlin we can use:
object DependencyVersions {
const val JETTY_VERSION = "9.4.12.v20180830"
}
dependencies{
implementation("org.eclipse.jetty:jettyserver:${DependencyVersions.JETTY_VERSION}")
}
Here, DependencyVersions is a name I chose. You can choose another name,
like "MyProjectVariables". This is a way to avoid using the extra or ext properties.
Global properties in kotlin-gradle-dsl:
https://stackoverflow.com/a/53594357/3557894
Kotlin version is embedded into kotlin-gradle-dsl.
You can use dependecies with embedded version as follows:
implementation(embeddedKotlin("stdlib-jdk7"))
classpath(embeddedKotlin("gradle-plugin"))
val junitVersion by extra("4.13.2")
testImplementation("junit:junit:$junitVersion")
In Kotlin, the way to do this is with by extra or an ext block.
With by extra:
val kotlinVersion = "95" by extra
val kotlinCompiler = true by extra
With ext:
ext {
set("kotlinVersion", "95")
set("kotlinCompiler", true)
}
Set it like this:
val kotlinVersion by extra("1.1.4")
Use it like this:
val kotlinVersion: String by rootProject.extra
It's a possibility to define global properties within gradle.properties:
xyzVersion=1.0.0
And then use them in your module's build.gradle.kts:
val xyzVersion: String by project

How are gradle extra properties set in the Kotlin DSL?

I'm trying to organize my build files as I would in groovy, by having values in a separate file to reuse. But I cannot understand the syntax to do the same thing in the kotlin DSL.
Here's what I'm using in root build.gradle.kts:
applyFrom("config.gradle.kts")
buildscript {
repositories {
google()
jcenter()
}
dependencies {
val test = project.extra["minSdkVer"]
classpath("com.android.tools.build:gradle:3.0.0-alpha4")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.2-5")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}
and here's whats in the config.gradle.kts that is being referenced:
mapOf(
Pair("minSdkVer", 22),
Pair("targetSdkVer", 25),
Pair("compiledSdkVer", 25),
Pair("buildToolsVer", "26-rc4")
).entries.forEach {
project.extra.set(it.key, it.value)
}
But there's an error:
Cannot get property 'minSdkVer' on extra properties extension as it
does not exist
A correct fix: Gradle collects and applies the buildscript { ... } blocks from your script strictly before executing anything else from it. So, to make your properties from config.gradle.kts available inside the buildscript, you should move applyFrom("config.gradle.kts") to your buildscript { ... } block:
buildscript {
applyFrom("config.gradle.kts")
/* ... */
}
Another possible mistake is using an extra property as extra["minSdkVer"] in a scope of another ExtensionAware, like a task in this example:
val myTask = task("printMinSdkVer") {
doLast {
println("Extra property value: ${extra["minSdkVer"]}")
}
}
In this case, extra.get(...) uses not the project.extra but the extra of the task.
To fix that, specify that you work with the project. Direct usage:
println(project.extra["minSdkVer"])
And for delegation.
val minSdkVer by project.extra

Resources