How to properly register custom tasks entirely in Java?


(Thorsten Schöning) #1

I have some build.gradle using two custom tasks and would like them to be completely implemented and created etc. in Java in some external build-project. I would like to only use them in build.gradle in some e.g. dependsOn, like in the following example. The goal is that nothing else of ´newEmptyDeploymentDiris placed inbuild.gradle` in the end.

publish.dependsOn build, newEmptyDeploymentDir

While my current implementation works that way, the approach needs some additional plugin to be able to actually create the tasks under their names for the current project. Only creating some subclass of DefaultTask or such doesn’t seem to be enough, the task needs to be created using the TaskContainer of some project and that is only accessible using a plugin AFAIK.

Is that correct or is there some other way using tasks only to register/create/… new tasks? I currently don’t need the plugin for anything else but to create the tasks. One example of one of my tasks, that function is called in apply of my plugin:

public static void configure(Project project)
{
	TaskContainer	tasks	= project.getTasks();
	JavadocToJar	me		= tasks.create(JavadocToJar.MY_NAME, JavadocToJar.class);
	Javadoc			javadoc	= (Javadoc) tasks.getByName(JavadocToJar.JD_NAME);


	me.dependsOn(javadoc);
	me.setClassifier(JavadocToJar.JD_NAME);
	me.from(javadoc.getDestinationDir());
}

One of my tasks depends on extra properties providing some paths and using the above approach I wasn’t able to access those extra properties because there where not available yet at the time the plugin has been applied. I was able to reimplement that task to only access those properties in the action executed using doLast, but that made me wonder if I’m doing something wrong.

So if one is forced to create a plugin to create tasks, how to I distinguish the different phases? I guess there is some API at project level to put some actions into the different lifecycle phases of a project, like doLast for tasks? I couldn’t find it.

Thanks!


(Chris Doré) #2

Custom task classes should have a method annotated with @TaskAction. That method will be executed during the task execution phase.
For example:

public class JavadocToJar extends DefaultTask {
    @TaskAction
    void doWork() {
        getLogger.lifecycle( "From my custom task" );
    }
}

(Thorsten Schöning) #3

I’ve done that as necessary, my question is about if my approach creating the tasks is correct or if there’s some specialized API to use. Your example and that from the docs create the tasks and model their dependencies in build.gradle, which is not what I want.

And if one creates tasks in Java as I am, one needs to distinguish the different phases of the build, especially configuration vs. execution. But I couldn’t find proper API/callbacks/… for that.


(Chris Doré) #4

Sorry, I misunderstood what you were asking.

Your approach is correct. The apply method of your plugin will be called when your plugin is first applied to a given project, which will be during Gradle’s configuration phase.
The language used to implement the plugin does not change when or how a plugin’s apply method is called.


(Thorsten Schöning) #5

Please consider the following contents in some build.gradle:

// This is only a JAR, so managing old, binary style.
apply plugin: 'ams_docbeam_raw_server'
apply plugin: 'ams_eclipse_default_code_style'
[...]
ext
{
	svnBinDir	= "${rootDir}/../../../Svn/Bin"
	deployDir	= "${svnBinDir}/DocBeam3/trunk/Docbeamserver/Programme/Versand-Server/lib"
}

The plugins are applied to a time when ext has not yet been processed, correct? Is there some way in the plugins to defer some e.g. creation of tasks to after ext has been processed?

The approach I’m currently using is accessing ext only during the runtime of the task. But one might need some infos of ext to configure a task earlier. Is Project.afterEvaluate the correct API to use or is that too late already?


Share constant between build.gradle and plugin
(Chris Doré) #6

Correct, the plugin’s apply will be called first. Yes, afterEvaluate could be used to defer your plugin’s configuration code until after those extra properties have been set. However, if I was using your plugin I could also do the following:

apply plugin: 'ams_docbeam_raw_server'
apply plugin: 'ams_eclipse_default_code_style'
afterEvaluate {
    ext {
        svnBinDir = "${rootDir}/../../../Svn/Bin"
        deployDir = "${svnBinDir}/DocBeam3/trunk/Docbeamserver/Programme/Versand-Server/lib"
    }
}

Which would leave you back where you started.

In general, you’ll want your tasks to defer their use of a value until task execution time. Depending on what versions of Gradle your plugins need to support, there are a few options for implementing this available to you. What version(s) do you need to be compatible with?


(Thorsten Schöning) #7

And that’s easily possible with what I need currently, I just thought of situations where I need to dependsOn some task named by some ext or such.

I just started to use Gradle using 4.10, so that or the most current one would be enough already.


(Chris Doré) #8

I recommend you consider using the Property and Provider functionality in the new Gradle versions:
https://docs.gradle.org/current/userguide/lazy_configuration.html
https://guides.gradle.org/implementing-gradle-plugins/