Custom Task/Plugin in gradle - groovy

I'm trying to create a custom Task/Plugin (both refuse to work) to use in my gradle build script.
I'm using the groovy plugin and want to declare the Tasks/Plugins in a separate files and not inside my build.gradle.
My project tree is the following:
/project
.
|-gradle
|-src
|---main
|-----groovy
|-----java
|-----resources
|---test
|-----groovy
|-----java
|-----resources
|-build.gradle
What I tried to do, is create my Task/Plugin classes inside src/main/groovy and then use them in my build.gradle.
Let me give a small example.
src/main/groovy/mypackage/TestTask.groovy:
package org.gradle.mypackage
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
public class TestTask extends DefaultTask {
#TaskAction
def doAction() {
}
}
build.gradle
plugins {
id 'groovy'
}
sourceCompatibility = 1.7
repositories {
mavenCentral()
}
dependencies {
compile(
localGroovy(),
gradleApi()
)
testCompile(
[ group: 'junit', name: 'junit', version: '4.11' ]
)
}
task testTask(type: TestTask)
When I try to do anything using my gradle.build (clean, build, etc), I get the following error:
Error:(116, 0) Could not find property 'TestTask' on root project 'project'.
What am I doing wrong? I tried to import the Task in build.gradle using import mypackage.TestTask but that didn't work either.
It looks to me like the groovy files do not compile at all while from what I read in the docs gradle should take care of compiling and adding them in the classpath.

Here you go: "in-stackoverflow" solution.
In your project create folder buildSrc/src/main
Don't forget to create build.gradle in buildSrc/
apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
Now in the main folder create package with your task, for example: pl.myAwesomeCompany.gradle.tasks
package pl.myAwesomeCompany.gradle.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
class ExampleTask extends DefaultTask {
#TaskAction
void execute(IncrementalTaskInputs inputs) {
println inputs.incremental ? "CHANGED inputs considered out of date"
: "ALL inputs considered out of date"
}
}
Voila! Now in your project you can use your plugin:
import package pl.myAwesomeCompany.gradle.tasks.ExampleTask
task incremental(type:ExampleTask)

This is not how it works. If you need to provide custom plugins and tasks organized in packages , put the whole code under buildSrc directory. Under $GRADLE_HOME/samples/multiProjectBuildSrc You can find excellent example how it should be done.
$GRADLE_HOME - gradle installation directory.

Related

How can a custom gradle task be ran from the body of a build

I have the following custom plugin:
class GenPlugin implements Plugin<Project> {
void apply(Project project) {
project.task("gen", type:GenTask)
}
}
That adds the following task to the project:
class GenTask extends DefaultTask {
#TaskAction
def gen() {
println "hello"
}
}
The groovy code is packaged and deployed to a local maven repository, I then reference the dependency in a build with:
apply plugin: 'gen'
I can run the task successfully by using gradle gen, how can I run this task in my build.gradle without specifying it as an argument?
You can use defaultTasks, have a look at the docs.
Running the task from gradle itself is highly discouraged. You can do it with:
project.tasks.gen.execute()
however I don't recommend you to go that way. Instead you can define a dependency on the task you run from command line. If this task is called e.g. afterGen, then use:
afterGen.dependsOn gen

Create a Groovy executable jar with Spock test set as to be executed

I want to create jar with two groovy files, AppLogic.groovy which consists of two few groovy classes and another file, AppSpec that has Spock test suite and I would like to have this Spock class executed (set as executable). How can I create such jar with all dependencies? I found sth similar for jUnit here: how to export (JUnit) test suite as executable jar but could not adapt it for my needs.
I use gradle for build, here is my build.gradle file:
group 'someGroup'
version '1.0'
apply plugin: 'groovy'
apply plugin: 'java'
apply plugin:'application'
sourceCompatibility = 1.7
repositories {
//some repos here
maven { url "http://repo.maven.apache.org/maven2" }
}
dependencies {
//some dependencies here
}
I was browsing around and found SpockRuntime, but I do not know if and how I can use it to achive my goal.
And the winner is:
static void main(String[] args) {
EmbeddedSpecRunner embeddedSpecRunner = new EmbeddedSpecRunner()
embeddedSpecRunner.runClass(MySpec)
}
I do not advise using the EmbeddedSpecRunner from spock implementation as described in accepted answer.
This is what I found to work reliably with gradle 4.9. The basic approach is to use:
The gradle application plugin to create a single tarfile with all testRuntimeClasspath dependencies and shell scripts to run the spock tests
The gradle maven-publish plugin to publish the tar file as an artifact to your maven repo (in my case nexus)
The build.gradle file looks like this:
apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'maven-publish'
apply plugin: 'application'
mainClassName = 'org.junit.runner.JUnitCore' // The junit 4 test runner class
applicationName = 'run-tests-cli' // Feel free to change
repositories {
...
}
dependencies {
...
testImplementation "org.codehaus.groovy:groovy-all:${groovyVersion}"
testImplementation "org.spockframework:spock-core:${spockVersion}"
}
// Package compiled spock / junit tests to <artifact>-test-<version>.jar
task testJar(type: Jar) {
classifier = 'tests'
from sourceSets.test.output.classesDirs
}
// Copy all testRuntimeClasspath dependencies to libs folder
task copyToLibs(type: Copy) {
from configurations.testRuntimeClasspath
into "$buildDir/libs"
}
// Make sure test jar is copied
copyToLibs.dependsOn('testJar')
// Make sure platform-specific shell scripts are created after copyToLibs
startScripts.dependsOn(copyToLibs)
// Configure what goes into the tar / zip distribution file created by gradle distribution plugin assembleDist task
distributions {
main {
contents {
// Include test jar
from(testJar) {
into "lib"
}
// Include all dependencies from testRuntimeClasspath
from(copyToLibs) {
into "lib"
}
}
}
}
startScripts {
// Ensure ethat all testRuntimeClasspath dependencies are in classpath used by shell scripts
classpath = project.tasks['testJar'].outputs.files + project.configurations.testRuntimeClasspath
}
publishing {
repositories {
maven {
def releasesRepoUrl = "https://nexus.yourcompany.com/repository/maven-releases/"
def snapshotsRepoUrl = "https://nexus.yourcompany.com/repository/maven-snapshots/"
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
credentials {
username = rootProject.getProperty('NEXUS_USERNAME')
password = rootProject.getProperty('NEXUS_PASSWORD')
}
}
}
publications {
maven(MavenPublication) {
groupId = 'com.yourgroupId'
version = "${rootProject.getVersion()}"
}
TestJar(MavenPublication) {
artifact(testJar)
}
RunTestsCliTar(MavenPublication) {
artifact(distTar)
artifactId "${applicationName}"
}
}
}
Now you can do the following:
To build the project (including the tar file) without running test task: gradle -x test clean build
To publish artifacts produced by project (including tar file to maven repo - in my case nexus): gradlew -x test publish. Note you will need to provide credentials to upload artifacts to repo. It is good practice to define them (NEXUS_USERNAME, NEXUS_PASSWORD in my example) in ~/.gradle/gradle.properties or specify them via -P options on the gradle command line.

Fixing Gradle Artifactory plugin publishing issue

I have a multi-project build that I am building with Gradle:
myapp/
myapp-client/
myapp-shared/
myapp-server/
build.gradle
settings.gradle
Where settings.gradle looks like:
include ':myapp-shared'
include ':myapp-client'
include ':myapp-server'
I have successfully got Gradle to compile my Groovy source code, run unit tests, generate GroovyDocs, and package both binary and source JARs for all 3 subprojects. The build invocation for which is: gradle clean build groovydoc sourcesJar -Pversion=<whatever version I specify>.
I am now attempting to add the Gradle-Artifactory plugin such that:
All 3 subprojects get POMs generated for them; and
All 3 subproject binary JARs, POMs and source JARs get published to my locally-running Artifactory; and
The artifactoryPublish task executes whenever gradle build is invoked
Here's my best attempt (my complete build.gradle):
allprojects {
buildscript {
repositories {
maven {
url 'http://localhost:8081/artifactory/plugins-release'
credentials {
username = "admin"
password = "password"
}
name = "maven-main-cache"
}
}
dependencies {
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:3.0.1"
}
}
apply plugin: 'groovy'
apply plugin: 'maven'
apply plugin: 'maven-publish'
apply plugin: "com.jfrog.artifactory"
version="0.0.1"
group = "mygroup"
repositories {
mavenCentral()
add buildscript.repositories.getByName("maven-main-cache")
maven {
url "http://localhost:8081/artifactory/mydev-snapshots"
}
}
artifactory {
contextUrl = "http://localhost:8081/artifactory"
publish {
repository {
repoKey = 'mydev-snapshots'
username = "admin"
password = "password"
maven = true
}
defaults {
publications ('mavenJava')
}
}
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
}
}
rootProject {
artifactoryPublish.skip=true
}
subprojects {
apply plugin: 'groovy'
apply plugin: 'eclipse'
sourceCompatibility = '1.7'
targetCompatibility = '1.7'
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
repositories {
mavenLocal()
mavenCentral()
maven {
url "https://repository.apache.org/content/repositories/snapshots"
}
maven {
url "http://localhost:8081/artifactory/mydev-snapshots"
}
maven {
url "https://code.google.com/p/guava-libraries/"
}
}
dependencies {
compile (
'org.codehaus.groovy:groovy-all:2.3.7'
)
}
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
task wrapper(type: Wrapper) {
gradleVersion = '1.11'
}
build(dependsOn: 'artifactoryPublish')
}
When I run gradle clean build groovydoc sourcesJar -Pversion=0.1.1, I get the following command line exception:
FAILURE: Build failed with an exception.
* Where:
Build file 'C:\Users\myuser\sandbox\eclipse\workspace\myapp\build.gradle' line: 14
* What went wrong:
A problem occurred evaluating root project 'myapp'.
> You can't change a configuration which is not in unresolved state!
* 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: 2.589 secs
My question: what is going on here, and what do I need to do (specifically) to fix it and get the Artifactory plugin publishing?
Bonus question: I'm specifying version number in 2 places (the build invocation as well as inside the build.gradle file. I want to specify version number only via the build invocation. How do I configure artifactoryPublish (or rather, the Gradle-Artifactory plugin) to accept the version I specify from the command-line?
Number of issues here:
buildscript should be top-level block, not inside allprojects
When using Artifactory, you don't need to specify any other repositories except of Artifactory (don't need mavenCentral())
If you want to use artifactoryPublish you need to configure the Artifactory plugin. Here are the docs and here are two fully working examples of multi-module Gradle projects: 1 and 2. Some highlights:
You need to apply maven or maven-publish plugin.
You need to add the produced artifacts to configuration or publication accordingly.
You need to configure the plugin with Artifactory instance you are working with, provide resolution and deployment repository names, credentials (usually for deployment only) and specify which configuration or publication you want to publish.

Specify ivy configuration in gradle dependency

I want to resolve dependencies from ivy repository but I don't know how to specify ivy configuration for it. I found that I should do it in this way:
myconf group: 'com.eu', module:'MyModule', version:'1.0.0', configuration: 'ivyconf'
but it doesn't work. When I run gradle dependencies command gradle returns this error:
Could not create a dependency using notation: {group=com.eu, module=MyModule, version=1.0.0, configuration=ivyconf}
My build doesn't use plugins. I want to download dependencies in simple build which should create product from downloaded dependencies.
Build looks like this:
group = 'com.eu'
version = '0.9a'
configurations {
myconf
}
repositories {
ivy {
url 'http://ivyrepo.local/ivyrep/shared'
layout "pattern", {
artifact "[organisation]/[module]/[revision]/[type]s/[artifact].[ext]"
}
}
}
dependencies {
myconf group: 'com.eu', module:'MyModule', version:'1.0.0', configuration: 'ivyconf'
}
Instead of module, it has to be name. (see "49.4. How to declare your dependencies" in the Gradle User Guide). The declared configuration (myConf) must match the configuration used in the dependencies block (installer).

Programmatically download dependencies in custom gradle task

I want to create task which depends on few maven libraries. Is it possible to download those libraries from groovy code? What I want to do is to put this code
configurations {
sshexecAntTask
}
repositories {
mavenCentral()
}
dependencies {
sshexecAntTask 'org.apache.ant:ant-jsch:1.7.0'
}
Which I use in this way:
ant.taskdef(name: 'sshexec', classname: 'org.apache.tools.ant.taskdefs.optional.ssh.SSHExec', classpath: project.configurations.sshexecAntTask.asPath)
ant.sshexec(host: host, username: username, password: password, command: command, trust: 'true', failonerror: 'true')
Into my DefaultTask class. Is it possible?
It should look similar to this:
class MyCustomTask extends DefaultTask {
public MyCustomTask() {
super()
// set and download dependencies here
}
}
[EDIT]
I've found that I can do it in this way:
project.getRepositories().mavenLocal()
project.getConfigurations().create('sshexecAntTask')
project.getDependencies().add('sshexecAntTask', 'org.apache.ant:ant-jsch:1.7.0')
project.getConfigurations().getByName('sshexecAntTask').resolve()
println('project.configurations.sshexecAntTask.asPath: '+project.getConfigurations().getByName('sshexecAntTask').getAsPath());
But it still doesn't work.
You would typically do something along the lines of:
MyCustomTask lives in its own build (possibly buildSrc).
The MyCustomTask project declares ant-jsch as a compile dependency.
The task's action (not constructor) defines (taskdef) and executes the Ant task. (Might have to be wrapped with project.ant.execute { ... }.)
Builds that wish to use MyCustomTask declare a build script dependency on its module (not necessary in case of buildSrc). Transitive dependency management automatically brings in ant-jsch along with it.
The customPlugin sample in the full Gradle distribution is a good place to get started. (Just declare a compile instead of a testCompile dependency.)

Resources