I'm having ordering issues with Gradle task configuration.
I'm writing a plugin that creates a task based on information that is only available when supplied via the build script. I can create the task in the plugin's apply() method, and configure it in its doFirst() method (by which time the necessary information is available). However, I also want to make sure that the task is only executed when necessary via the inputs/outputs properties.
This is problematic, because I have to do this in the apply() method (as far as I can tell), but at that point the information required to specify the inputs/outputs property values isn't available.
I tried using a convention object but that's not available under after apply() has completed.
Is there any way around this that won't make me feel dirty?
For those parts of the task configuration that need to be deferred, you can either use a hook like project.afterEvaluate {} or gradle.projectsEvaluated {}, or use convention mapping. The latter is used extensively by Gradle's own plugins, but isn't currently considered a public API.
Related
I have a Gradle plugin that works well so far, however, I am facing an issue in a multi-module project.
The plugin runs its task in every module of the project, but sometimes, I need to access classes from other modules to analyze them - typically, in the context of hexagonal architecture, I want to perform some checks on classes in infrastructure that implement an interface defined in domain module.
So, when the task runs (after the tests) within infrastructure module, it finds the class. But it's not able to load the interface (from domain) that the class implements. When I do :
Object interfaceLoadedDynamically = Class.forName(i.getName());
I clearly see it can't be loaded, and that prevents me from performing the checks I need to do.
here are :
the plugin
the task
Is there a way to configure one of them to say : "when you run within a module, make sure all the other project modules that have been compiled before are available in the classpath" ?
I've seen maybe I could use Gradle's Configuration , but I am really not sure where/how to start.
Like this other user, I have problems to understand the withType usage, example
tasks.withType(JavaCompile) {
options.compilerArgs.add("-Xbootclasspath/p:${file}")
}
... and differently from him, I have found official docs, as always, completely unuseful. Can you tell me what withType exactly does?
It finds all defined tasks in the current project that are of given type. You can have multiple tasks named and configured differently, but sharing the same type (and implementation details).
E.g. there is one task type JavaCompile that is then added as two tasks in standard Java project: compileJava and testCompileJava.
More could be found in Gradle docs. The syntax you ask about will give you possibility to set common config for all such tasks.
I usually define tasks in Gradle (using Groovy) like tasks.withType(Type); e.g.: tasks.withType(JavaCompile), tasks.withType(Test), etc.
Now, I want to do the same with some provided Spring Boot tasks, namely: bootRun and bootStartScripts, but Gradle cannot find it.
I know it's silly and I could get away just by using bootRun and bootStartScripts, but I would like to understand why those cannot be configured/defined in such way.
I guess with define you mean configure, because withType can only be used to configure existing tasks. It takes a task type (a class) and a closure that can be used to configure all available tasks of that type. This needs to be considered, because a project may contain multiple tasks of the same type that should actually do completely different things. Whether to configure all those tasks or just a specific one is important!
To pass the task type to the method withType you need to know the name of the class implementing the task type. This name is not necessarily related to the name(s) of the actual task(s). For the tasks test and compileJava of the Gradle Java Plugin those classes are org.gradle.api.tasks.testing.Test and org.gradle.api.tasks.compile.JavaCompile. Since those classes are provided by Gradle, they are automatically imported and can be referenced via their simple names Test and JavaCompile. But the Spring Boot Plugin is a third-party plugin, so the classes need to be referenced by their full names.
The task bootStartScripts from your question is of type CreateStartScript, that is provided by Gradle. Therefore it can be configured like this:
tasks.withType(CreateStartScripts) {
// configure
}
The task bootRun is of type org.springframework.boot.gradle.tasks.run.BootRun, that is provided by the Spring Boot Plugin. So you need to specify the full name:
tasks.withType(org.springframework.boot.gradle.tasks.run.BootRun) {
// configure
}
I often meet in Gradle documentation these concepts. What is the difference between them?
From what I know custom task is class that encapsulates some logic, but what is enhanced task and what is the difference between them?
A simple task in Gradle is an instance of DefaultTask and does not do anything out of the box. To make it do something you have to add code to the task definition in your build script.
An enhanced task is an instance of a different class (for example a Copy task is an instance of Copy) and has some behaviour out of the box. You just need to configure that behaviour in your build script (eg tell it where to copy from and to)
A custom task is an enhanced task that is an instance of a class you wrote yourself.
An enhanced task is defined in the Gradle manual as:
Gradle supports enhanced tasks, that is, tasks which have their own properties and methods. This is really different to what you are used to with Ant targets. Such enhanced tasks are either provided by you or are provided by Gradle.
It also says:
Gradle supports two types of task. One such type is the simple task, where you define the task with an action closure. We have seen these in Chapter 6, Build Script Basics. For this type of task, the action closure determines the behaviour of the task. This type of task is good for implementing one-off tasks in your build script.
The other type of task is the enhanced task, where the behaviour is built into the task, and the task provides some properties which you can use to configure the behaviour. We have seen these in Chapter 15, More about Tasks. Most Gradle plugins use enhanced tasks. With enhanced tasks, you don't need to implement the task behaviour as you do with simple tasks. You simply declare the task and configure the task using its properties. In this way, enhanced tasks let you reuse a piece of behaviour in many different places, possibly across different builds.
The behaviour and properties of an enhanced task is defined by the task's class. When you declare an enhanced task, you specify the type, or class of the task.
As part of writing a Gradle plugin for Flyway, we stumbled upon a problem when dealing with Java migrations.
What is the best way to provide a Gradle plugin access on its classpath to the compiled classes of the project so that it can load and execute them?
So the situation is that we have a plugin that adds a task that wants to execute code contained in the project that the plugin is applied to. In this case, the task (class) should have an input property of type Iterable<File> that gets configured (by the plugin) with the class path of the code to be executed (e.g. sourceSets.main.runtimeClasspath). The task can then choose between the following ways to execute the code:
The task uses project.javaexec {} to execute the code in a separate JVM. If the code isn't directly executable, the task may need to inject some bootstrap code onto the javaexec class path. A potential alternative to using project.javaexec is to use a JavaExec task in the first place.
The task creates a new class loader, populates it with the class path, loads and instantiates the classes that serve as the entry point(s) to the API, and makes use of them as appropriate. If the task is written in Groovy, it can leverage duck typing, and no reflective code will be necessary beyond creating the entry points.