Gradle def vs ext - gradle

What is the difference between using ext.varname and def varname. E.g. the following code seems to work the same:
task copyLicenses {
def outDir = project.buildDir.absolutePath + '/reports/license/'
doLast {
copy {
from 'licenses'
into outDir
include '*'
}
seems to work exactly the same as
task copyLicenses {
ext.outDir = project.buildDir.absolutePath + '/reports/license/'
doLast {
copy {
from 'licenses'
into outDir
include '*'
}

Keyword def comes from Groovy and means that variable has local scope.
Using ext.outDir means that you add property outDir to ExtraPropertiesExtension, think of it like project has a map of properties with name ext, and you put your property in this map for later access.

Related

Accessing Task properties in different subproject in Gradle

For the sake of asking this question, I have a trivial subproject defined as:
project("data-source-config") {
def projectName = "data-source-config"
jar {
baseName = "${projectName}_$scalaVersion"
version = "$version"
}
}
I would really like to be able to refer to the full output jar name in a separate subproject.
Something like:
project("another-subproject") {
def projectName = "another-subproject"
println(:data-source-config:jar.getProperty("archiveName")) // I don't know the correct syntax
}
I can see that the JAR task has the archiveName property in the docs here:
https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveFileName
Any guidance would be much appreciated!
project("another-subproject") {
evaluationDependsOn ':data-source-config'
println project(':data-source-config').tasks['jar'].archiveName
}

Re-using properties of custom gradle task

I'm defining gradle task like this:
task assembleAppPackage() {
File distDir = file("${projectDir}/dist")
File binDir = file("${distDir}/bin")
File configDir = file("${distDir}/config")
File libDir = file("${distDir}/lib")
doLast {
...using distDir , binDir, etc...
}
}
Somewhere later I want to add some functionality to assembleAppPackage, so I expect something like this to work:
assembleAppPackage {
doLast {
copy {
from "${projectDir}/bin"
into binDir #binDir from original task definition
}
}
}
And gradle claims there is no binDir in scope:
Could not get unknown property 'binDir' for object of type org.gradle.api.internal.file.copy.CopySpecWrapper_Decorated.
How to define task properties which could be later accessed on task extensions? Is it only possible with defining task class?
You can use extra properties for this I think:
task assembleAppPackage() {
ext.distDir = file("${projectDir}/dist")
ext.binDir = file("${distDir}/bin")
ext.configDir = file("${distDir}/config")
ext.libDir = file("${distDir}/lib")
doLast {
...using distDir , binDir, etc...
}
}
(rest of your code unchanged)
On most gradle entities you can use that concept: Set a property using "ext." or
ext {
name1 = value1
name2 = value2
}
See https://docs.gradle.org/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html or google for "gradle extra properties"

How to use a parameter in gradle copy task in the destination folder?

Given the following task in gradle, the dev would start a new module by:
gradle initModule -PmoduleName=derp
task initModule(type: Copy) {
description "Initialize an empty module based on the template. Usage: gradle initModule -P moduleName=derp"
onlyIf { project.hasProperty("moduleName") }
from "$rootDir/modules/template"
into "$rootDir/modules/$moduleName"
}
I am unable to run gradle since moduleName is not defined during evaluation, although I was hoping that "onlyIf" would do so.
To solve this I assign it to a locally defined variable in a guard block:
def modName = ""
if (!project.hasProperty("moduleName")) {
logger.error("Invalid moduleName : It can't be")
} else {
modName = moduleName
}
and finally use the new variable to survive the configuration phase.
Is this the right way to do this? It just doesn't feel right.
Additionally if I was using a rule to make this a tad more elegant:
tasks.addRule("Pattern: initModule_<mod name>") { String taskName ->
if (taskName.startsWith("initModule_")) {
def params = taskName.split('_');
task(taskName) {
doLast {
project.ext.moduleName = params.tail().head()
tasks.initModule.execute()
}
}
}
}
The moduleName is not passed around (even if I change it to finalizedBy).
Any help is appreciated.
As you already figured out the property is not available during the configuration phase.
But can postpone the look-up to the execution phase by using
into "$rootDir/modules/" + project.property("moduleName")
instead of
into "$rootDir/modules/$moduleName"

Gradle - publish artifacts

I want to publish artifacts to ivy repository but it doesn't work. I read this article and after read created this sample build:
task ivyPublishTest << {
def buildDir = new File("build")
buildDir.mkdirs()
def fileToPublish = new File("build/file.abc")
fileToPublish.write("asdasdasd")
}
artifacts {
archives(ivyPublishTest.fileToPublish) {
name 'gradle-test-artifact'
builtBy ivyPublishTest
}
}
uploadArchives {
repositories {
ivy {
url "http://my.ivy.repo/ivyrep/shared"
}
}
}
Of course the problem is that it doesn't work. I get this error Could not find property 'fileToPublish' on task ':ivyPublishTest'
In Groovy, def creates a local variable, which is lexically scoped. Therefore, fileToPublish is not visible outside the task action. Additionally, configuration has to be done in the configuration phase (i.e. the declaration and assignment of fileToPublish in your task action would come too late). Here is a correct solution:
task ivyPublishTest {
// configuration (always evaluated)
def buildDir = new File("build")
ext.fileToPublish = new File("build/file.abc")
doLast {
// execution (only evaluated if and when the task executes)
buildDir.mkdirs()
fileToPublish.write("asdasdasd")
}
}
artifacts {
// configuration (always evaluated)
archives(ivyPublishTest.fileToPublish) {
name 'gradle-test-artifact'
builtBy ivyPublishTest
}
}
ext.fileToPublish = ... declares an extra property, a new property attached to an existing object that's visible everywhere the object (task in this case) is visible. You can read more about extra properties here in the Gradle User Guide.

How can I override a property defined in build.gradle?

How can I set a property in the build.gradle file and allow each developer to override it locally? I tried:
gradle.properties:
MY_NAME = "Jonathon"
MY_COLOR = "blue"
build.gradle:
ext.MY_NAME = "John Doe"
task showit <<{
println "MY_NAME[" + MY_NAME + "]";
println "MY_COLOR[" + MY_COLOR + "]";
}
gradle showit gives:
:showit
MY_NAME[John Doe]
MY_COLOR["blue"]
I thought that a property defined in a gradle.properties file at the project root would override a property with the same name defined in build.gradle, but this does not appear to be the case. It only fills in for a missing property.
Check whether the project has a property, and if not, set it to the default value:
ext {
if (!project.hasProperty('MY_NAME')) {
MY_NAME = 'John Doe'
}
}
See: https://docs.gradle.org/current/userguide/build_environment.html#sub:checking_for_project_properties
If you need to do this for multiple properties, you can define a function:
def setPropertyDefaultValueIfNotCustomized(propertyName, defaultValue) {
if (!project.hasProperty(propertyName)) {
ext[propertyName] = defaultValue
}
}
ext {
setPropertyDefaultValueIfNotCustomized('MY_NAME', 'John Doe')
}
Similar to Johan Stuyts' answer, but as a one-liner:
ext.MY_NAME = project.properties['MY_NAME'] ?: 'John Doe'
Running gradle -PMY_NAME="Jeff F." showit gives MY_NAME[Jeff F.].
Running gradle showit gives MY_NAME[John Doe].
i think you can define a local variable and then override it like this
def dest = "name"
task copy(type: Copy) {
from "source"
into name
}
see this doc
I just ran into this myself. My use case is that I fork project repos and my coworkers don't. I needed to set the repository remote for axion-release to upstream instead of origin.
I didn't want to have to pass args (or create aliases/shell functions) so I ended up creating a gradle.properties in my GRADLE_USER_HOME (~/.gradle) and adding a property (name doesn't matter) for this there as well as in the project's gradle.properties. In gradle.build I always use that property since it always exists.
Example
~/.gradle/gradle.properties
scmVersionRepositoryRemote=upstream
[project-root]/gradle.properties
scmVersionRepositoryRemote=origin
[project-root]/build.gradle
scmVersion {
repository {
remote = scmVersionRepositoryRemote
}
}

Resources