29

It there any way to avoid duplication in configuration between two similar tasks of the same type?

For example, I'd like to create a debugSomething task, with the same configuration as runSomething below but with the addition of a remote debugger argument to the jvmArgs:

task runSomething(dependsOn: jar, type: JavaExec, group: "Run") {
    jvmArgs "-Xmx1024m", "-XX:MaxPermSize=128m"
    main = "com.some.Main"
    classpath = sourceSets.main.runtimeClasspath
}
David Pärsson
  • 6,038
  • 2
  • 37
  • 52
  • I know i could extract the configuration in variables, but since I have several environment and command line arguments to set, this does not seem ideal to me. – David Pärsson Oct 25 '12 at 15:35

4 Answers4

33

I've found that using the Task.configure method is very helpful for centralizing logic like this.

I haven't tested it, but in your case this might look like this:

def commonSomething = {
    main = "com.some.Main"
    classpath = sourceSets.main.runtimeClasspath
    jvmArgs "-Xmx1024m", "-XX:MaxPermSize=128m"
}

task runSomething(dependsOn: jar, type: JavaExec, group: "Run") {
    configure commonSomething
}

task debugSomething(dependsOn: jar, type: JavaExec, group: "Run") {
    configure commonSomething
    jvmArgs ...add debug arguments...
}
Alan Krueger
  • 4,701
  • 4
  • 35
  • 48
  • It works fine, but when adding a `dependsOn` to the `commonSomething` closure Eclipse says that it is deprecated. Is it Eclipse that improperly evaluates this to a `Project.dependsOn` instead of a `Task.dependsOn`? – David Pärsson Jan 29 '13 at 11:06
  • `commonSomething` is just a closure passed to configure, not a task definition. I believe when you call dependsOn in there, you're calling the Project.dependsOn method, which is indeed deprecated. http://gradle.org/docs/current/javadoc/org/gradle/api/Project.html#dependsOn(java.lang.String) – Alan Krueger Jan 29 '13 at 15:11
  • 1
    Ok, so to clarify: When defining common dependencies for several, it's better to go with the `[tasks].each` approach. Right? – David Pärsson Jan 29 '13 at 16:25
  • If they both work, which one you choose probably boils down to taste. I [perhaps obviously] prefer the common closure method I posted. – Alan Krueger Jan 29 '13 at 16:48
8

This can be solved using plain Groovy:

task runSomething(dependsOn: jar, type: JavaExec, group: "Run") {
}

task debugSomething(dependsOn: jar, type: JavaExec, group: "Run") {
    jvmArgs "-agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=y"
}

[runSomething, debugSomething].each { task ->
    task.main = "com.some.Main"
    task.classpath = sourceSets.main.runtimeClasspath
    task.jvmArgs "-Xmx1024m", "-XX:MaxPermSize=128m"
}

Even though debugSomething.jvmArgs is invoked twice, all three arguments are supplied to the JVM.

Single arguments can be set using Groovy's Spread operator:

[runSomething, debugSomething]*.main = "com.some.Main"
David Pärsson
  • 6,038
  • 2
  • 37
  • 52
  • 2
    I've discovered that, inside the `each` closure, you can also say `task.with { ... }` to "switch contexts" to `task` -- you no longer have to repeat `task.` on each line, then. – Max Jun 24 '16 at 05:24
3

I've been searching for something similar with a difference that I don't want to share the config among all the tasks of the same type, but only for some of them.

I've tried something like stated in the accepted answer, it wasn't working well though. I'll try again then.

As I've got here, I don't mind to share, there is (at least now) a better, Gradle's built-in way to achieve the thing which was asked here. It goes like:

tasks.withType(JavaExec) {
    jvmArgs "-Xmx1024m", "-XX:MaxPermSize=128m"
    main = "com.some.Main"
    classpath = sourceSets.main.runtimeClasspath
}

This way all the tasks of JavaExec type will receive the default config which can obviously be altered by any specific task of the same type.

topr
  • 4,482
  • 3
  • 28
  • 35
1

Refer to section 51.2 of the manual. AFAICT, it shows exactly what you want.

Ed Staub
  • 15,480
  • 3
  • 61
  • 91
  • I was hoping there was a way to reuse an existing task definition (of an existing task type) rather than creating an entirely new task type, but this can indeed reduce some of the duplication. I created a new task type, setting the properties in the constructor. At this point, it's however not possible to get the sourceSets so that still leaves some duplication. – David Pärsson Oct 26 '12 at 07:47
  • @DavidPärsson - I'm a newb... but if you extended the existing type that you want to emulate (JavaExec) rather than DefaultTask, would that do what you need? – Ed Staub Oct 27 '12 at 00:04
  • Sorry if I was being unclear, that's exactly what I tried. It works for the static parts of the configuration, and that's at least better. If no better solution comes up, I'll accept yours. – David Pärsson Oct 29 '12 at 09:48
  • I tried to do something like this, but ran into project extra properties not being visible. It seemed different-enough from the above question that I created a new one: http://stackoverflow.com/questions/14530901/gradle-extra-properties-not-visible-in-a-custom-task-defined-in-a-subproject – Alan Krueger Jan 25 '13 at 21:49