HOWTO force a task to run before configuration of another task?


(Steve Cohen) #1

I want to write some plugin code that must run a system command to capture information that will be used in the configuration of a task.

I’ve tried the following steps:
1 configure that Task b depends on task a
2. Task a (Exec) runs the system command and stores the output
3. Task b reads the stored output from task a and uses it to configure itself

But of course, step 3 happens too late. What is the best way to make the configuration of task b update itself after the execution of Task a so it is correctly configured when it runs? Or is there a way to gather the information from the system without making a task out of it?

I’ve tried different forms of lazy evaluation and they haven’t worked.

Can someone point to an example where something like this is done?

Thanks.


(Steve Cohen) #2

I found this solution. Instead of the overkill of a TASK, just define a method, easy enough to do in groovy.

def getGitUrl() {
	"/usr/bin/git config --get remote.origin.url".execute().text.trim()
}

then in the configuration block of the task:

url=getGitUrl()

where url is a property of the task.

So that’s one answer. It works because the information is constant over the course of the build. Is there a more gradley way to do it?


(Chris Doré) #3

If you’d prefer to have the git command executed during task execution, I think you were on the right track with your original multi-task approach (commonly referred to as “configuration tasks”). Configuration tasks are ones that configure other tasks. So, instead of task B reading task A’s state, task A should configure task B. That way, any new inputs/outputs of B will be configured before task B is executed.

For example:

task doExpensiveConfiguration {
    doLast {
        tasks.doWork.calc = doLongExpensiveCalc()
    }
}

task doWork( type: MyTask ) {
    dependsOn tasks.doExpensiveConfiguration
}

class MyTask extends DefaultTask
{
    @Input
    String calc

    @TaskAction
    void doMyWork()
    {
        println calc
    }
}

(Steve Cohen) #4

Thanks for this solution. I don’t think I’ll use it because in my use case the configuration isn’t particularly expensive and it’s logically not going to change over the course of the build. It’s more or less a constant that is accessed by a system command. Unless you can offer some reason why what I’ve done is bad.

But your technique could be useful in some other situations. Configuration tasks. That’s a useful concept and I don’t think I’ve read about it in the User’s Guide. It ought to be there.


(uklance) #5

You can use a closure to delay evaluation.

Eg

task foo(type:MyTask) {
   calc = {
      Thread.sleep(2000)
      return 'foo' 
   } 
} 
class MyTask extends DefaultTask {
   @Input
   Closure calc

   @TaskAction
   void doMyWork() {
      println calc.call()
   } 
}

You might be interested in grolifant stringize which can help here by converting object to string, invoking a closure if provided. This lets the client choose rather than forcing them to use a closure


(Steve Cohen) #6

I am now trying to utilize your suggestion here and coming to grief. Could you look at java.lang.IllegalArgumentException: path may not be null or empty string. path=‘null’ and comment? This construct works fine for stuff like println calc, but not so well for copyspecs where I’m trying to use it.


(Steve Cohen) #7

-> Lazy Eval syntax can help. Another strategy is to have an actual reconfigure() method in the class being configured to be called from the doLast.

Instead of

task doExpensiveConfiguration {
    doLast {
        tasks.doWork.calc = doLongExpensiveCalc()
    }
}

You could have

task doExpensiveConfiguration {
    doLast {
        tasks.doWork.calc = doLongExpensiveCalc()
        tasks.doWork.reconfigure()
    }
}

...

void reconfigure {
   into ("/usr/local/${calc}/bin") {
     from('src\folder')
  }
}