How to export dependencies to different locations with Gradle? - gradle

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}/"
}
}
}

Related

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"

refer to Gradle task which is defined later in build

Our build has some generic logic to create tasks from configuration data. There are also several Gradle files on whose execution order I do not want to depend.
Now I need to add a task dependency to a task without knowing if that task will be defined before my part of the build script runs (at configuration time) or after my part.
[Edit: as #canillas writes I can say myTask.dependsOn "otherTask" and "lazily" depend on yet undefined "otherTask", but I need it the other way around.]
Since I can't write tasks["otherTask"].dependsOn myNewTask before "otherTask" is defined, I made the following helper function:
void safelyDoWithTask(String taskName, Closure func) {
def task = tasks.findByName(taskName)
if (task != null) {
func(task)
} else {
project.tasks.whenTaskAdded { task_ ->
if (taskName.equals(task_.getName())) {
func(task_)
}
}
}
}
safelyDoWithTask('otherTask', { it.dependsOn myNewTask })
Now I wonder if there is more canonical way to achieve this?
Consider the following:
// section 1
def dynamicDependencies = [:]
dynamicDependencies['otherTask'] = 'myNewTask'
// section 2
afterEvaluate { project ->
// taskA dependsOn taskB
dynamicDependencies.each { taskA, taskB ->
def task = project.tasks.findByName(taskA)
if (task) { task.dependsOn "${taskB}" }
}
}
// section 3
task myNewTask() {
doLast {
println 'myNewTask !'
}
}
task otherTask() {
doLast {
println 'otherTask !'
}
}
The gist is:
section 1 defines our dependency info in a custom map
section 2 uses the afterEvaluate hook to process the map
because the above is decoupled from the task definitions, we can simply put them in section 3 (or wherever)

Communication between gradle methods and tasks

I can't seem to get my collection from the method to a state where generateWadleTasks can iterate over it. At least not in a way such that I can access it. I've tried properties, parameters and return values. What's wrong with this picture? I seem to have some misconceptions about how information is passed around either in Groovy or in Gradle... or perhaps both. An explanation of the scopes involved might help. This is a subproject.
buildscript{
...
}
ext.collectionOfObjects = []
class WadleProfile {
def File wadleFile;
def wadlArgs;
}
ext.factoryMethod = {
//supposed to create and add WadleProfiles to collectionOfWadleProfiles
}
compileJava {
doFirst { factoryMethod() }
}
task generateWadlTasks {
println "Executing generateWadlTasks"
project.ext.collectionOfObjects.each { wadlProfile ->
task("wadl2java" + wadlProfile.id, type: JavaExec, dependsOn: 'classes') {
main = project.wadlBaseMain
...
}
}
}

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"

Is it possible to dynamically create source sets?

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") {
...
}
}

Resources