I'm trying to write a custom Gradle plugin using Java. One of the things I'll need to do within this plugin is copy all the defined dependencies into a folder. I've found out how you can do this using a task in the build.gradle file:
task copyDeps(type: Copy) {
from configurations.runtime
into 'build/lib'
}
However, I'm struggling to figure out how I would run this task from within my own custom task. I've tried doing this:
public class MyTask extends DefaultTask {
#TaskAction
public void executeTask() {
Copy copyTask = new Copy();
copyTask.into(libFolder).from(getProject().getConfigurations().getByName("runtime").execute();
}
}
However, when I run this I get the exception:
Task of type 'org.gradle.api.tasks.Copy' has been instantiated directly which is not supported. Tasks can only be created using the DSL.
I understand what it is trying to say, but I haven't been able to find out I would be able to execute this task using the DSL from within Java.
You can't create tasks within tasks in Gradle. If you want to write a custom plugin with a task as you have described, the way to go is to write a custom plugin class and within that plugin class you declare a task of type Copy that does what you want to do. This could look like this:
class MyCustomPlugin extends Plugin<Project> {
void apply(Project project) {
project.tasks.create("copyDeps", Copy.class) {
from configurations.runtime
into 'build/lib'
}
}
}
Now, if you apply this plugin within your buildscript:
apply plugin:MyCustomPlugin
you automatically have a task named "copyDeps" of type Copy in your build.
One more thing:
Sometimes it can be convenient to do a simple copy operation in a custom task. This can be done using the project.copy util method. A task like this would look this
public class MyTask extends DefaultTask {
#TaskAction
public void executeTask() {
project.copy {
into(libFolder)
from(project.configurations.runtime)
}
}
}
With plain java it would look like this:
public class MyTask extends DefaultTask {
#TaskAction
public void executeTask() {
project.copy(new Action<CopySpec>() {
#Override
void execute(CopySpec copySpec) {
copySpec.into(libFolder);
copySpec.from(getProject().getConfigurations().getByName("runtime"));
}
});
}
}
One last thing:
You should never call Task#execute() your own. This method is considered to be internal API in Gradle and executing it directly causes unexpected behaviour. (for example it breaks Gradles' incremental build feature)
Related
Trying to create a custom gradle plugin in java, how do i get the resources path from inside the task class?
public class MyCustomPlugin implements Plugin<Project> {
#Override
public void apply(Project project) {
project.getTasks().register("doStuff", CustomTask.class);
}
}
public class CustomTask extends DefaultTask {
// How do I get java project resources dir from here?
#Inject
public CustomTask(ProjectLayout projectLayout) {
directoryProperty = projectLayout.getBuildDirectory();
}
#TaskAction
public void execute() {
...
}
}
I would recommend to not get the directory inside the task, because the plugin that provides it might not be applied. Instead I would do it from within your plugin that registers the task, this way you can also ensure that the necessary plugin is actually applied. Gradle will display an error if the task is used without a value being assigned to the input that explains that nothing was assigned.
With the kotlin-dsl:
#CacheableTask
abstract class CustomTask : DefaultTask() {
#get:InputFiles
abstract val resources: FileCollection
//...
}
I cannot answer if #InputFiles is the right annotation for your use case, because I don't know what you want to do with the resource. Refer to the Gradle documentation for more information on the available annotations, and what they do.
plugins {
java
}
tasks.register<CustomTask>("customTask") {
resources.set(sourceSets.main.map { it.resources })
}
Notice the map {} which ensures that our task has a dependency on the processResources task, this is done automatically for us because we stick to the provider API of Gradle for everything.
Note that the resources are by default in one directory, but they don't have to be. This is why the resources are defined as SourceDirectorySet and not as Provider<Directory>. The same is true for anything that originates from the SourceSetContainer. It is easier to explain with Java source code: imagine you have Java and Kotlin, then you will have src/main/java and src/main/kotlin, hence, 2 directories. The former will have a **/*.java include filter, whereas the latter has a **/*.kt includes filter. If we just want to get all sources then we use sourceSets.main.map { it.java.sourceDirectories }, and if we want to get one of both it gets complicated. 😝
First, you'd have to ensure this is a Java project: either applying the "java" plugin from your plugin (project.getPluginManager().apply("java")), or only registering the task when the "java" plugin has been applied by the user (project.getPluginManager().withPlugin("java", ignored -> { project.getTasks().register(…); });).
You could then get the resources from the main source set:
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
// Use named() instead of get() if you prefer/need to use providers
SourceSet mainSourceSet = sourceSets.get(SourceSet.MAIN_SOURCE_SET_NAME);
SourceDirectorySet resources = mainSourceSet.getResources();
BTW, the best practice is to have tasks only declare their inputs and outputs (e.g. I need a set of directories, or files, as inputs, and my outputs will be one single file, or in one single directory) and have the actual wiring with default values be done by the plugin.
You could have the plugin unconditionally register the task, then conditionally when the "java" plugin is applied configure its inputs to the project resources; or conditionally register the task or unconditionally apply the "java" plugin, as I showed above.
You can access the sources through the project.sourceSets.
#Inject
public CustomTask(Project project) {
directoryProperty = project.projectLayout.getBuildDirectory();
sourceSet = project.sourceSets.main
}
See also the reference documentation here: https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_project_layout
When creating a task with the name jar, Gradle automatically knows that the class of the task is org.gradle.api.tasks.bundling.Jar. How can I replicate this with my custom task?
i.e. I have the following class:
class MyTaskType extends DefaultTask {
#Input String name
// Options
#TaskAction
def generateImage() {
// Stuff
}
}
Up until now I've been doing the following:
task veryCoolTaskName(type:MyTaskType) {
name 'some-name'
}
And I want to be able to define a task of this type by writing:
myTaskType {
name 'some-name'
}
Just like with jar {...}. How can I do that?
This is not how Gradle works.
When writing jar { ... }, you don't actually create a new task. Instead, you are refererring to an existing task jar that was created by a plugin (e.g. the Java plugin):
plugins {
id 'java'
}
jar {
// This works because the task was created by the plugin
}
Without any plugin applied, the same excerpt will fail, as a task named jar does not exist:
jar {
// This fails, because the task `jar` does not exist
}
Instead, you need to create the task to configure it:
task jar(type: Jar)
jar {
// This works because the task was created beforehand
}
Of course, the creation and the configuration of the task may be combined:
task jar(type: Jar) {
// ...
}
Okay, I'm not sure how to go about explaining this, nor how to do it, but I will try to explain what I want step-by-step.
I want to make an API that contains, for example an EntitySpawnEvent object. It might look something like this:
namespace ExampleAPI
{
class EntitySpawnEvent
{
private bool cancelled;
private Entity entity;
public EntitySpawnEvent(Entity entity)
{
this.entity = entity;
this.cancelled = false;
}
public void SetCancelled(bool cancelled)
{
this.cancelled = cancelled;
}
public bool IsCancelled()
{
return this.cancelled;
}
}
}
Then I have I will have a server that uses this API. This server will also load plugins that also uses the API. The server might be something like this:
using System.Generics;
using ExampleAPI;
namespace ExampleServer
{
class Server
{
private List<Plugin> plugins;
public OnEnable()
{
LoadPlugins();
}
private void LoadPlugins()
{
// Loop through all "plugins" in the "/plugins" folder.
// Add them all to the list of plugins.
}
}
}
Then later when the server wants to spawn an entity, it throws the event to all plugins, the plugins can manipulate the event's information. For example, whether or not to cancel the event. The plugin's event listener could look something like this:
using ExampleAPI;
namespace ExamplePlugin
{
class Plugin : EventListener
{
public void onEntitySpawn(EntitySpawnEvent event)
{
event.SetCancelled(true);
}
}
}
And the server would throw it something like this:
using ExampleAPI;
namespace ExampleServer
{
class ExampleEventThrower
{
private Server server;
public ExampleEventThrower(Server server)
{
this.server = server;
SpawnEntity();
}
void SpawnEntity()
{
EntitySpawnEvent event = new EntitySpawnEvent(new Entity()); // Entity would also be part of the API
foreach (Plugin plugin in server.GetPlugins())
{
plugin.onEntitySpawn(event); // Here the plugin could manipulate the values of the EntitySpawnEvent
}
if (!event.IsCancelled())
{
// Spawn entity
}
}
}
}
Of course these are just extremely basic code examples, but they should help explain what I want.
Basically, what I want to know and do is the following:
I have an exported Server.
The Server have a /plugins folder
The user can make their own plugins using the API, export them and put them in the /plugins folder
The Server would load the plugin and let it modify all the events and such.
My key question is, how should the plugins be exported and loaded, so they can manipulate the events and such? Do I export them as DDLs? I have no idea.
I guess it's sort of similar to the way Bukkit works, but there everything's in Java and you just export it is a .jar file.
Any help would be greatly appreciated. Thanks!
So few things to take a look at...
It sounds like you want to have plugins run off of an interface that you know about, and load the plugins at runtime.
This should help you build the DLLs:
http://msdn.microsoft.com/en-us/library/3707x96z.aspx
This should help load the DLL dynamically at runtime:
Can I load a .NET assembly at runtime and instantiate a type knowing only the name?
I'm creating a custom ant task, which performs an IO tasks based on the user received param(like an file write/append)
I wanted to write the task so as if the developer using it in the ant task runs it with a -v or -d flag, will output more,
I'm wondering how are the core ant tasks doing it. Are they checking the output level before printing to console or is it just done by using java.util.logging.Logger
Follow this tutorial.
Extract :
Integration with TaskAdapter
Our class has nothing to do with Ant. It extends no superclass and
implements no interface. How does Ant know to integrate? Via name
convention: our class provides a method with signature public void
execute(). This class is wrapped by Ant's
org.apache.tools.ant.TaskAdapter which is a task and uses reflection
for setting a reference to the project and calling the execute()
method.
Setting a reference to the project? Could be interesting. The Project
class gives us some nice abilities: access to Ant's logging facilities
getting and setting properties and much more. So we try to use that
class:
import org.apache.tools.ant.Project;
public class HelloWorld {
private Project project;
public void setProject(Project proj) {
project = proj;
}
public void execute() {
String message = project.getProperty("ant.project.name");
project.log("Here is project '" + message + "'.", Project.MSG_INFO);
} }
[...]
I'm writing a MyTask that extends org.apache.tools.ant.Task
In the execute() method of MyTask i need to include a file.
I mean i would call the <include> task in the execute() method of MyTask.
I looked at the Apache Ant API but i didn't found the class IncludeTask that implements the <include> task
Where can i find the Include java class?
It seems that <include> isn't implemented as Task class in the normal way. The logic seems baked into org.apache.tools.ant.ProjectHelper, as though <include> is handled in a special way.
You may not have much luck trying to leverage that functionality.
Taken from: http://ant.apache.org/manual/develop.html
For example suppose one wanted to handle objects object of type org.apache.tools.ant.taskdefs.condition.Condition, one may have a class:
public class MyTask extends Task {
private List conditions = new ArrayList();
public void add(Condition c) {
conditions.add(c);
}
public void execute() {
// iterator over the conditions
}
}
One may define and use this class like this:
<taskdef name="mytask" classname="MyTask" classpath="classes"/>
<typedef name="condition.equals"
classname="org.apache.tools.ant.taskdefs.conditions.Equals"/>
<mytask>
<condition.equals arg1="${debug}" arg2="true"/>
</mytask>