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"
Related
I have some gradle script where i read some properties file (differen file in different execution configurations) and assign Properties obect to "ext" property in each task.
task provisionMongoDBCopyDockerfile(type: Copy, dependsOn: 'readTestConfiguration') {
from "${projectDir}/deployment/scripts/Dockerfile.mongodb"
into "/tmp/stand/mondodb"
expand(ext.stand)
filteringCharset = 'UTF-8'
}
task readTestConfiguration () {
def props = loadStandProperties('test')
println props
tasks.each {
it.ext.stand = props
println it.ext
}
}
but when i run gradle script i get this error: "Cannot get property 'stand' on extra properties extension as it does not exist" in line with "expand(ext.stand)". How can i solve this problem. I don't want to put all configuration parameters in "gradle.properties" and change it from configuration to configuration.
Consider the following (using Gradle 2.14.1). This effectively sets up a dependency in Configuration phase. Also it uses project.ext versus tasks.ext.
def readTestConfiguration = {
def props = loadStandProperties('test')
println props
project.ext.stand = props
}
def loadStandProperties (def env) {
// use mock data
return ["foo": "bar"]
}
tasks.whenTaskAdded { Task task ->
if (task.name == "provisionMongoDBCopyDockerfile") {
readTestConfiguration()
}
}
task provisionMongoDBCopyDockerfile(type: Copy) {
from "${projectDir}/in"
into "${projectDir}/out"
expand(project.ext.stand)
}
I wanted to avoid creating different configurations because I would like to have many different behaviors associated with my dependencies, not just this one.
So thanks to 'ext' properties I am able to associate my own data to the dependencies:
runtime 'org.apache.logging.log4j:log4j-api:2.5'
runtime 'org.apache.lucene:lucene-core:5.4.1'
runtime('hsqldb:hsqldb:1.8.0.10') { ext.exportTo = 'jdbc' }
Now I want to exploit it to copy the dependencies into 'libs/' by default, and 'libs/${exportTo}/' if exportTo is specified.
task copyRuntimeLibs(type: Copy) {
into "build/dependencies"
from configurations.runtime
}
So I'm trying to create a dynamically created configuration for those special dependencies, and a dynamically created task to do the job, which I link to the main task 'copyRuntimeLibs':
configurations.runtime.allDependencies.withType(ExternalModuleDependency).each { d ->
if (d.hasProperty("exportTo")) {
def key = "${d.group}:${d.name}:${d.version}"
def configname = "config-${key}"
def c = project.configurations.create(configname)
c.getDependencies().add(d)
def task = task "task-${key}"(type: Copy) {
into "build/dependencies/${d.ext.exportTo}"
from c
}
project.tasks.add(task)
copyRuntimeLibs.dependsOn task
}
}
So the dynamic tasks is executed, however nothing is copied. When I add a 'eachFiles' to debug, it doesn't go there.
Any clue?
task copyRuntimeLibs
copyRuntimeLibs << {
configurations.runtime.allDependencies.withType(ExternalModuleDependency).each { d ->
def exportFolder = 'build/dependencies'
def exportSub = ''
if (d.hasProperty("exportTo")) {
exportSub = d.ext.exportTo
}
def key = "${d.group}:${d.name}:${d.version}"
def c = project.configurations.create("config-${key}")
c.getDependencies().add(d)
copy {
from c
into "${exportFolder}/${exportSub}/"
}
}
}
If there is a build.gradle file as follows:
...
apply from: 'Other.gradle'
task hello {
project.ext.hello = "hello"
}
And Other.gradle has:
task getHello {
println project.ext.hello
}
I get an error saying:
Cannot get property 'hello' on extra properties extension as it does not exist
Is there a way to share property extensions between the scripts?
Try setting ext.hello then have the tasks update it
== build.gradle
ext {
hello = null
}
apply from: 'Other.gradle'
task hello {
doLast {
hello = "hello"
}
}
== Other.gradle
task getHello {
doLast {
println hello
}
}
If you really want to be able to set info on a task, you can also use the ext on a task and scope it to a task. If you were implementing a larger plugin you could create an extension and set it on the task.
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"
I am wondering if it is possible to dynamically create a source set with gradle. The directory hierarchy of my current project looks as follows:
dbfit-junit
module
foo
bar
Each of this module folders (foo and bar) should have its own source set assigned. The reason is that I want to dynamically create tasks like dbFitTestFoo and dbFitTestBar. My current approach looks like this:
ext.dbFitModulesDir = "dbfit-junit/module"
ext.dbFitTestSpecs = ["java", "groovy", "scala", "resources"]
ext.dbFitModules = []
file(dbFitModulesDir).eachDir{ module ->
dbFitModules << module.name
}
/** this needs to be done dynamically for each "module" **/
sourceSets {
integrationTest { sourceSet ->
dbFitModules.each{ module ->
dbFitTestSpecs.each { spec ->
if (!sourceSet.hasProperty(spec)) {
return
}
sourceSet."$spec".srcDir file("$dbFitModulesDir/$module/$spec")
}
}
}
}
dbFitModules.each{ module ->
task "dbFitTest${module.capitalize()}"(type: Test) {
group = "Verification"
description = "Run dbFit tests for $module"
doLast {
testClassesDir = sourceSets.integrationTest.output.classesDir
classpath = sourceSets.integrationTest.runtimeClasspath
}
}
}
The creation of the tasks works smoothly, the only thing that is still missing is the dynamic creation and assignment of the sourcesets.
Thanks for any hints!
Yes, you can create source sets dynamically. Here's one example:
dbFitModules.each { module ->
sourceSets.create("${module}Test") {
...
}
}