How to embed Java code into Gradle build using JavaExec task - java

I have a Gradle-driven project to which I want to add a simple Java task. This task is very specific to the project and if it can be helped I don't want to develop it as a separate plugin. So the question is can I define such custom task within the same build.gradle I'm using for my project? Or is it inevitable that I need to package it as a separate project (plugin) and install to the local repo?
Also it's probably important to note that the original project is not Java related (no other Java code needs to be build)
P.S. Based on comments below:
I would like to add src/main/java/SomeUsefulStuff.java to the existing project and have that file compiled and used as a custom task. I do understand that it needs to be compiled each time I run the build but again - the code will be small. However it will have some external dependencies such as Commons IO

Thanks to RaGe who pointed to JavaExec this turned out to be pretty simple. Here's what you do:
Put your Java code in /src/main/java just as you would in the regular Gradle-driven Java project. Make sure it has main method in the file you are going to call
Add apply plugin: 'java' to the build.gradle
If your Java code has any dependencies on 3rd party libs add these to dependencies section
Add new task section to build.gradle like so:
task usefulStuff(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
main = 'com.me.gradle.UsefulStuff'
// arguments to pass to the application
args 'OhmyGod!'
}
Now you can refer to that task as any task in your build. For example imporantTask.dependsOn usefulStuff

Related

Multiple steps for spotless format java error while gradle build

There is an IntelliJ project using Gradle, Java and in one of the modules, I put the Java code which is working independently. But on moving the code within this project and running the build.gradle I am getting the following error:
Could not create task ':wlcr-auto-api:spotlessJavaApply'. Could not
create task ':wlcr-auto-api:spotlessJava'. Multiple steps with name
'google-java-format' for spotless format 'java'
I am not sure what is the reason for this error. Also, I am not sure if the project structure is correct in Intellij.
In the above image auto is the project that was already created. I created a new module auto-api and put all my code inside it. There is an Application.java inside it, the main file to run the spring boot application. It works when the code is not put inside this auto project. But not when included in the project.
I have no clue how to debug this.
check out your build.gradle - probably you missed something inside block:
spotless {
...
}
at least, if you simply remove it - the issue will gone

Added Gradle to Java project "Exception ... java.lang.NoClassDefFoundError"

I had an existing project without Gradle and needed to add com.google.code.gson:gson:+ library to work with JSON objects. To begin with I ran either gradle init or gradle build, I'm not sure. This caused my java classes with a main() not to run as the source path was wrong/changed. I have changed the structure following advice to at least get the classes to compile and run, but I still have this warning in run configurations "Warning: Class 'Main' not found in module 'src'" ;
If I set Use classpath of module to src.main, the warning goes away but when I run Main.main() Gradle seems to execute Gradle tasks, like this - this will run indefinitely;
Here is my project structure;
This is my build.gradle file;
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java project to get you started.
* For more details take a look at the Java Quickstart chapter in the Gradle
* User Manual available at https://docs.gradle.org/6.3/userguide/tutorial_java_projects.html
*/
plugins {
// Apply the java plugin to add support for Java
id 'java'
// Apply the application plugin to add support for building a CLI application.
id 'application'
// idea plugin? // I added this to original build.gradle file
id 'idea'
}
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
mavenCentral()
google()
}
dependencies {
// This dependency is used by the application.
implementation 'com.google.guava:guava:28.2-jre'
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
// For use with JSONUtil class // I added this to original build.gradle file
compile 'com.google.code.gson:gson:+'
}
application {
// Define the main class for the application.
mainClassName = 'java.Main' // changed to 'Main' and I can `gradle run` seems to actually run Main.java
}
I have imported com.google.gson.JsonObject and com.google.gson.JsonParser from com.google.gson:gson:2.8.6 library, with no code inspection warnings, i.e available at compile time. If I run my code with a JsonObject jsonObject = new JsonObject I get the error;
Exception in thread "main" java.lang.NoClassDefFoundError: com/google/gson/JsonParser
at HttpUtils.getAccessToken(HttpUtils.java:80)
at Main.auth(Main.java:75)
at Main.play(Main.java:36)
at Main.main(Main.java:17)
Caused by: java.lang.ClassNotFoundException: com.google.gson.JsonParser
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 4 more
Line 80 of HttpUtils.java;
JsonObject jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject(); // todo: status 200 "success" else failed
accessToken = jsonResponse.get("access_token").getAsString();
System.out.println(accessToken);
I understand this means that JVM can't compile a .class for JsonParser? I suppose this means the compiler has no knowledge of the library existing, which makes me suspect that Gradle isn't configured properly with the project, as it has downloaded the library, but not added a path to it?
I have tried gradle cleanIdea and then gradle idea. I have rebuilt the the project. I have "Mark directory as source root" on various directories for testing being careful to revert when it failed to change behaviour.
Edit;
I have added a package com.example in the src.main.Java directory and added the java files.
I edited run configuration for Main.java to
Main class: com.example.Main
Use classpath of module: src.main
I also changed the build.gradle file to;
application {
// Define the main class for the application.
mainClassName = 'com.example.Main'
}
Main runs but I am stuck at this point, which seems to run indefinitely;
Also, I am sure I right clicked on build.gradle and selected import, although I can't recreate this as the option isn't available now.
Edit 2;
I have been able to get the classes Main and Test with main() to run by putting them in the test/java/src package, and using unusual run configuration with warnings. Although on closer inspection, it seems to be running code that is previously compiled somewhere, as any changes I make aren't reflected in output.
Here is my project structure at the moment;
This is my run configuration that actually runs main in the standard output console, rather than a Gradle Task. It's clearly wrong, as Main is not in the com.example package or src.main module. If I set it correctly using module src.test and main class src.Main Gradle runs as screenshot 5.
Edit 3;
I see now that Gradle has took over responsibility to build and run the java files. I didn't know running in the output could be done with another CLI app and I admit it confused me, so please forgive anything above that seems stupid, I'm learning and figuring this out as I go.
I found in InteliJ settings Build, Execution, Deployment > Build Tools > Gradle I can change the Build and run using option between InteliJ IDEA and Gradle. The only issue I'm having with Gradle now I understand what is happening is Gradle doesn't seem to update my .class files when I run my main() with Gradle. Maybe this is for another question though.
mainClassName = 'java.Main' // changed to 'Main' and I can "gradle run" seems to actually run Main.java
This is not correct. Based on screenshot - you have not package named java (also I doubld that this is a valid name for a Java package). Create proper package inside src/main/java directory and specify it in the Main source file and in build.gradle file.
Also make sure you have imported build.gradle file in IDE, see Link a Gradle project to an IntelliJ IDEA project

create gradle task in java based plugin

I have a java based gradle plugin that does some common configurations for our gradle projects. Now I want to add tasks like (currently present in our build.gradle for each project)
task javadocJar(type: Jar) {
classifier = "javadoc"
from javadoc
}
What I need is a hint on how to do this in a java based gradle plugin.
In another plugin I already register tasks using project.getTasks().create("myTask", MyTask.class); where MyTask extends AbstractTask and has a #TaskAction method to do its duty. But I'm found no way to adapt this to work with what i want to do with the sample above.
I tried to google for a solution but until now i did not find a helpfull soution as everything i find is using groovy or something similar to my snipped above directly in the build.gradle.
Thanks in advance!
Looks like I found a solution, at least for the javadoc task...
Task javadocTask = project.getTasks().getByName("javadoc");
project.getTasks().create("javadocJar", Jar.class, task -> {
task.dependsOn(javadocTask);
task.setClassifier("javadoc");
task.from(javadocTask);
});
this seems to work... but now i have an issue with the next gradle task:
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
until now i haven't found a way to get a hand on the sourceSets varialbe in Java.
Thanks in advance! :)
response to asettoufs comment:
The docs you have linked are related to projects with sub projects. What I have are multiple single projects that are not related. For those we have a plugin that already applies some plugins and configures them. so the projects have a common configuration by just applying our plugin.
Our plugin is written in java. And should continue to be java. Now we want to move some more common build config stuff to the plugin - for example some of those basic tasks as above.
To get your the source set you can ask the convention
project.getTasks().create("sourceJar", Jar.class, task -> {
task.setClassifier("sources");
// grab the convention that holds the sourceSets
JavaPluginConvention javaConvention = project.getConvention().getPlugin(JavaPluginConvention.class);
// find our source set
SourceSet mainSourceSet = javaConvention.sourceSets.findByName('main');
// user `from` like normal
task.from(mainSourceSet.getAllSource());
});
Some source code to see how the JavaPlugin creates the sourceSet written in Java
Creating the java convention
https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlugin.java#L266
Creating the sourceSets
https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlugin.java#L298

File dependency Gradle

I am a gmake user transitioning to Gradle. I have a multi-project structure, where one sub-project is a Java project and the other a home-brewed language. The home-brewed language does not use any Gradle plugins. Now I want to add a task that runs a Java program to generate XML when any of my home-brewed source files have been modified. In make, I would just declare a dependency on inputFile.mine or *.mine next to the target name, but I could not easily find how to do this basic thing with Gradle. Currently, I force the task to always execute using the potentially ugly work-around below. I want to replace this with some dependsOn *.mine . The Gradle user guide has a whole chapter dedicated to explaining different ways of specifying files, but I did not see how to declare a dependency.
task generateXML(type: Exec) {
generateXML.getOutputs().upToDateWhen({false}) // Force it to execute always
executable("java.exe")
args("-jar", "resources/generateXml.jar", "src/inputFile.mine")
}
Thanks for helping a newbie out.
You can define task inputs and outputs in Gradle.
For example:
task generateXML(type: Exec) {
inputs.file ("src/inputFile.mine")
executable("java.exe")
args("-jar", "resources/generateXml.jar", "src/inputFile.mine")
}
See https://docs.gradle.org/current/userguide/more_about_tasks.html and https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskInputs.html for more information.
Side note: When you run your build with -i, Gradle will tell you what has happened during the up-to-date check.

Gradle: how to copy subproject jar to another subproject when task is run?

I have the following multiproject structure in Gradle:
- root-project
- acceptance-tests
- sub-java-proj
acceptance-tests has a task cucumber (provided by Gradle Cucumber-plugin) which runs the Cucumber features in the project. However, I need to do some bootstrapping before the features are run. In particular, before thecucumber task Gradle should do the following:
Run tests in sub-java-proj and if they pass produce its jar file (if not fail)
Copy the jar file from 1) into the directory accentance-tests/proglib
Run the Cucumber features like normal
Delete the jar file copied in 2)
I'm completely new to Gradle so I am not sure how to properly implement this. My first thought was to make acceptance-tests depend on sub-java-proj (with testRuntime config), but I feel this is an abuse of the dependency mechanism: the only reason to do this is to ensure sub-java-proj is compiled before acceptance-tests is run, but in reality this also messes with the classpath of acceptance-tests by adding the jar from sub-java-proj and there's also problems with transitive dependencies. In short: acceptance-tests should not have anything to do with sub-java-proj besides copying it's jar around.
Suggestions on how to accomplish this are very much appreciated.
Update
So far I have the followingin acceptance-tests/build.gradle:
task fix(dependsOn: ':sub-java-proj:jar', type: Copy) {
from tasks.getByPath(':sub-java-proj:jar')
to 'proglib'
}
task unfix(type: Delete) {
delete fix
}
cucumber.dependsOn fix
cucumber.finalizedBy unfix
However, this works except that unfix deletes the proglib directory too. It should only delete the jar file. Any ideas how to fix this?

Categories

Resources