gradle: how to write a groovy task - gradle

I've been trying to work through the user guide. This seems like an incredibly simple thing I want to do, but I just can't figure out how to make it work.
I want to write an external groovy file to define a task, then call that task in build.gradle. Here's my build.gradle file:
// Apply the groovy plugin to add support for Groovy
apply plugin: 'groovy'
// In this section you declare where to find the dependencies of your project
repositories {
// Use 'jcenter' for resolving your dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
}
// In this section you declare the dependencies for your production and test code
dependencies {
// We use the latest groovy 2.x version for building this library
compile 'org.codehaus.groovy:groovy-all:2.4.7'
compile gradleApi()
compile localGroovy()
// We use the awesome Spock testing and specification framework
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile 'junit:junit:4.12'
}
task myTask (type: my.package.MyTask)
Then, under src/main/groovy/my/package I have MyTask.groovy:
package my.package
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
class MyTask extends DefaultTask {
String greeting = 'hello from MyTask'
#TaskAction
def greet(){
println greeting
}
}
I can't even list the tasks:
$ gradle tasks
FAILURE: Build failed with an exception.
* Where:
Build file '/projects/gradle/build.gradle' line: 21
* What went wrong:
A problem occurred evaluating root project 'gradle'.
> Could not get unknown property 'my' for root project 'gradle' of type org.gradle.api.Project.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 3.193 secs
At this point, I have absolutely no idea what's going on or how to do what I want. This seems like the most simple possible example but following the user guide has gotten me nowhere. No matter what I do, I can't get past that exception.
How do I write a groovy file and use that to define a new task in the build file?

You have to place your groovy files under:
rootProjectDir/buildSrc/src/main/groovy
Reference:
https://docs.gradle.org/current/userguide/custom_tasks.html#N14573
EDIT
I never tried changing the location of buildSrc but it appears to be an unresolved issue:
https://issues.gradle.org/browse/GRADLE-2816
(correct it if I'm wrong)

Now that you've got your src moved to buildSrc you'll also need to fix a small typo in MyTask.groovy:
Instead of
import org.gradle.api.tasks.DefaultTask
use
import org.gradle.api.tasks.TaskAction
That ought to fix your #TaskAction annotation.

Related

gradle default configuration, what is it and how can I define it

I am having a weird error, for which I found a lot of hits in google but most about android studio or library imports and I don't use android studio nor am I trying to build any app/library so I'll try and ask here and see if I understand the background of it.
I have a project in which I have this plugin:
https://gitlab.com/zkovari/gradle-mermaid-plugin/-/blob/master/examples/single-project/build.gradle
This is a plugin that displays my dependencies as a mermaid graph.
Everything was fine until today I updated some dependencies and created a sub-project to do something else.
I'm trying to generate the graph again and somehow it's failing with this error message.
> Task :generateMermaidDependenciesDiagram FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':generateMermaidDependenciesDiagram'.
> Configuration with name 'default' not found.
I can see in the source code of the plugin that it automatically sets some configurations
#Override
public void apply(Project project) {
GenerateMermaidDependenciesDiagram generateDependencyDiagramsTask = project.getTasks()
.create(GENERATE_MERMAID_DEPENDENCIES_DIAGRAM_TASK_NAME, GenerateMermaidDependenciesDiagram.class);
generateDependencyDiagramsTask.setConfiguration("default");
generateDependencyDiagramsTask
.setOutput(project.getBuildDir().toPath().resolve("mermaid/dependencies.mmd").toFile());
generateDependencyDiagramsTask.setDiagramGenerator(new MermaidDiagramGenerator());
}
So my question is a bit high level but what is the "default" configuration?
And in my project I don't really have any configuration block I simply have my dependencies:
dependencies {
implementation group: 'com.amazonaws', name: 'aws-java-sdk-s3', version: awsVersion, transitive: false
...
testCompile group: 'org.slf4j', name: 'slf4j-simple', version: slf4jVersion
}
My structure looks like this:
project:
-- build.gradle
-- settings.gradle
-- sub-project:
--- build.gradle
I think this stopped working when I added my sub-project but the plugin is applied at the root project level.
I do have a dependency between my sub-project and my root project (one of my sub-project tasks depends on the root project)
I'm trying to understand that this might be very similar but I'm not fully getting it.
Gradle: What Is The Default Configuration and How Do I Change It
I do apply the java plugin on my main project so I expect the configuration.default should exist.
Even if I think I shouldn't do it I tried to re-define a default configuration extending implementation but that did not work
https://docs.gradle.org/current/userguide/declaring_dependencies.html
configurations {
default.extendsFrom implementation
}
build file 'C:\dev\repo\connector-custom\build.gradle': 66: unexpected token: default # line 66, column 5.
default.extendsFrom implementation
I did a quick test and removing my subproject did work so I know the problem is there but no idea why.
It means somehow I must pass to my subproject the default configuration of the main project?

How to execute gradle task during project import in Intellij Idea

Let's assume my build.gradle file contains task generateSources which as name suggests generates additional java files. It's easy to ensure that generateSources is executed before compileJava: compileJava.dependsOn generateSources. How can I make sure generateSources is called when importing project into Intellij Idea as well?
To elaborate on #vladimir-sitnikov's answer: I added the idea-ext-plugin to my root project:
apply plugin: 'org.jetbrains.gradle.plugin.idea-ext'
// ...
buildscript {
dependencies {
classpath "org.jetbrains.gradle.plugin.idea-ext:org.jetbrains.gradle.plugin.idea-ext.gradle.plugin:0.7"
}
}
Because without that I wasn't able to use it in my sub project, but now it works like this:
idea.project.settings.taskTriggers {
beforeSync tasks.getByName("generateSources")
}
Adding the plugin to the sub-project only didn't do it.
Note: The plugin's documentation is kind of limited, but in "DSL spec v. 0.2" is stated
beforeSync - before each Gradle project sync. Will NOT be executed on initial import
Didn't try that, but it works with existing projects.
This can be done via id("org.jetbrains.gradle.plugin.idea-ext") plugin (https://github.com/JetBrains/gradle-idea-ext-plugin).
See sample code in Gradle sources: https://github.com/gradle/gradle/blob/135fb4751faf2736c231636e8a2a92d47706a3b9/buildSrc/subprojects/ide/src/main/kotlin/org/gradle/gradlebuild/ide/IdePlugin.kt#L147
You can set the task in Gradle tool window: Execute Before Sync:

Utilize ant in settings.gradle during configuration phase

I want to apply a shared gradle file to my projects settings.gradle. The shared file is located in a jar which must be downloaded and extracted during the configuration phase. This is because is applies a plugin which must be applied in the configuration phase. I found this related question: How to share a common build.gradle via a repository? My preferred way is described in this answer: https://stackoverflow.com/a/39228611/987860
However, this appears to be working in build.gradle only. I tried to move the buildscript block to my settings.gradle.
settings.gradle
buildscript {
ext {
dependencyVersion = '0.1.2'
}
repositories {
maven {
credentials {
username 'user'
password 'password'
}
url 'https://my-private-maven-repo.com'
}
}
dependencies {
classpath "my.group:myartifact:$dependencyVersion"
}
dependencies {
def gradleScripts = new File(rootDir, '/build/gradle')
delete gradleScripts
def jars = configurations.classpath.files as List<File>
ant.unjar(src: jars.find { it.name.matches '.*myartifact.*' }, dest: gradleScripts) {
patternset {
include(name:'*.gradle')
}
}
}
}
apply from: new File(rootDir, '/build/gradle/myscript.gradle')
But this results in the following exception:
FAILURE: Build failed with an exception.
* Where:
Settings file 'settings.gradle' line: 24
* What went wrong:
A problem occurred evaluating settings 'journal'.
> Could not get unknown property 'ant' for object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 0.019 secs
Could not get unknown property 'ant' for object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.
Is there any way to utilize ant int the confiuration phase before my settings.gradle is evaluated? I need to have the dependency downloaded and extractet before the to-be-downloaded file gets applied.
This is a really unusual way to do things. I'd really recommend not doing what you're trying to do because it'll make your build much slower than it should be. You're deleting build/gradle and extracting the contents of the plugin's jar on every build.
Everything inside a build.gradle (or settings.gradle) can be put into a plugin and distributed that way. You already have a jar that needs to be downloaded, so converting myscript.gradle into a plugin is very easy to roughly convert.
Put this in src/main/groovy/some/package/MyPlugin.groovy in the project that's producing the plugin jar already:
package some.package
import org.gradle.api.*
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.with {
// contents of script
}
}
}
For plugins applied to settings.gradle:
package some.package
import org.gradle.api.*
class MyPlugin implements Plugin<Settings> {
void apply(Settings settings) {
settings.with {
// contents of script
}
}
}
Then you can just add the dependency to the plugin and use apply plugin: some.package.MyPlugin.
There are a lot of other advantages of developing/distributing plugins in this way. You can find more information on plugin development in the Gradle Guides.
Alternatively, if you absolutely must keep the separate .gradle script. If you can serve it separately (outside of the jar), you can do:
apply from: "http://example.com/some/url/myscript.gradle"
The downside with this is that it'll download the file on every build (this is fixed in Gradle 4.2).

Gradle: Fail on adding systemProperty

I'm trying to add a .dll file to the "java.library.path" system property via gradle on my Spring Boot project. I'm using Gradle 2.1 on STS.
This is the small piece of groove code within my build.gradle:
tasks.withType(JavaCompile) {
systemProperty "java.library.path", file("./src/main/resources/META-INF/opencv-2.4.9/windows_bin/x64")
}
And I'm getting the following error:
Could not find method systemProperty() for arguments [java.library.path, D:\GitHub\TFG_1\GuiaTV\src\main\resources\META-INF\opencv-2.4.9\windows_bin\x64] on root project 'GuiaTV'
That path does exists, so I don't know where the problem is.
Any help? Thank you!
UPDATE 1:
#Amnon Shochot
What I try to do is to add a native library (.dll) to the project. I took the idea from some sites (for example, http://zouxifeng.github.io/2014/07/17/add-system-property-to-spring-boot.html, https://github.com/cjstehno/coffeaelectronica/wiki/Going-Native-with-Gradle).
The first one is using what you suggested:
tasks.withType(JavaExec) {
systemProperty "java.library.path", file("./libs")
}
The second one is using:
run {
systemProperty 'java.library.path', file( 'build/natives/windows' )
}
None of them are working for me.
The first one (with JavaExec) is failing gradle test throwing:
java.lang.UnsatisfiedLinkError: no opencv_java249 in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1865)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
If you follow the trace, it's crashing at runtime in sentence: System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
And the second one is failing on gradle build with the following message:
Could not find method run() for arguments [build_24sfpo0st6dokeq7fn3ad7r34$_run_closure7#2652c3da] on root project 'GuiaTV'.
Luckily you know exactly what I try to achieve and you can solve my problem.
Thank you for your interest!
UPDATE 2:
Finally, I ended up adding these lines to my build.gradle script:
// The following makes "gradle build", "gradle test" work
test {
jvmArgs = ['-Djava.library.path=./src/main/resources/META-INF/opencv-2.4.9/windows_bin/x64']
}
// Thw following makes "gradle run" work
run {
jvmArgs = ['-Djava.library.path=./src/main/resources/META-INF/opencv-2.4.9/windows_bin/x64']
}
By the way, I'm also using "spring-boot" gradle plugin. That's where the run task comes from.
So, I can execute "gradle build", "gradle test" and "gradle run" sucessfully. This is, that native library is correctly added.
However, since I'm also using "eclipse" gradle plugin, I would like to add the native library simply by executing "gradle eclipse". Instead, I must create the library on Eclipse manually, and add it to my project.
Thank you #Amnon for your collaboration. I'll be posting a new solution in the case I found it.
The problem is that you do not set the context for the systemProperty method thus Gradle tries to locate it in the project object where it does not exist which is the reason for the error you got.
If you wanted to apply this configuration for all tasks of type JavaCompile your code should have been looked like:
tasks.withType(JavaCompile) { JavaCompile t ->
t.systemProperty "java.library.path", file("./src/main/resources/META-INF/opencv-2.4.9/windows_bin/x64")
}
However, the JavaCompile task type also does not contain a systemProperty so this code wouldn't work either.
You can define CompileOptions for a JavaCompile task using its options property, i.e.:
tasks.withType(JavaCompile) { JavaCompile t ->
t.options "java.library.path", file("./src/main/resources/META-INF/opencv-2.4.9/windows_bin/x64")
}
However, I'm not sure whether you can define this specific system property.
One last note - the systemProperty method does exist for tasks of type JavaExec in case that this is what you intended to do.

How to compile single class dependency in gradle outside the main project

I have a gradle project that contains only Selenium/TestNG test classes. They are executed against a deployed war application. All works fine and now I'm adding a java utility that will query the test base and print list of tests that belong to a given TestNG group. The utility should be compiled and executed separate from the main project, as users may want to query the test base before test execution.
I added the following to build.gradle:
task listgroups(dependsOn:'buildUtil' ) <<{
ant.java(classname: 'util.TestGroupScanner', fork: true,
classpath: "src/test/java")
}
task buildUtil {
compile {
source = "src/test/java/util"
}
}
However, when calling listgroups task, I'm getting the following error:
C:\console-bg1>g listgroups
FAILURE: Build failed with an exception.
(...)
* What went wrong:
A problem occurred evaluating root project 'console-bg1'.
> Could not find method compile() for arguments [build_4emu7duna2isgubc1k8uts8k9
8$_run_closure6_closure11#d210ab] on root project 'console-bg1'.
I'm not sure how to resolve this issue and needless to say, haven't found an answer online so far. Any pointers appreciated.
The problem is in the buildUtil task, as the error suggests. The buildUtil declares a compile closure, but such closure does not exist for the default task.
Let me try to clarify what your setup is. The util.TestGroupScanner source is in the src/test/java/util directory, which you want to compile separately from other source (presumably src/main/java and src/test/java). The buildUtil task is supposed to compile sources in src/test/java/util, and the listgroups task executes the scanner utility on sources src/test/java folder.
In this case, I'd suggest you declare a new source set for your utility sources, like this:
sourceSets {
util {
java {
srcDir 'src/test/java/util'
}
}
}
This will automatically create a compile task called compileUtilJava for you, that will compile those sources. I also think you'll want to include utility classes in the classpath when executing your tool, which can be retrieved by sourceSets.util.output.classesDir. So now your listgroups task will look like:
task listgroups(dependsOn: 'compileUtilJava' ) <<{
ant.java(classname: 'util.TestGroupScanner', fork: true,
classpath: "src/test/java:" + sourceSets.util.output.classesDir)
}
One thing I have noticed about your setup, is that src/test/java/util source folder is nested under src/test/java. Gradle will assume src/test/java to be the default folder for your project test, and will therefore automatically include it, and all of its children when running tests. Since you want to keep your utility folder separate from the default setup, I would recommend you put it in src/testutil/java, to avoid any clashes. If you do, don't forget to update the sourceSets setup above with the correct source path.
To solve this with gradle, I suggest to create a specific sourceset for your util class and add a task of type JavaExec that executes this class for printing your testng groups. Have a look at the following snippet:
apply plugin:'java'
...
...
configurations{
testUtilCompile.extendsFrom testCompile
}
...
...
sourceSets{
testUtil{
java {
srcDir "src/test/java"
include "util/**"
}
}
}
task printGroups(type:JavaExec){
main = "util.TestGroupScanner"
classpath = sourceSets.testUtil.runtimeClasspath
}
regards,
René

Resources