Grails gradle "a task with that name already exists" - gradle

I'm trying to create a test task rule using the example provided in the grails gradle doc but I keep getting "a task with that name already exists" error.
My build script is as follows:
import org.grails.gradle.plugin.tasks.* //Added import here else fails with "Could not find property GrailsTestTask"
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "org.grails:grails-gradle-plugin:2.0.0"
}
}
version "0.1"
group "example"
apply plugin: "grails"
repositories {
grails.central() //creates a maven repo for the Grails Central repository (Core libraries and plugins)
}
grails {
grailsVersion = '2.3.5'
groovyVersion = '2.1.9'
springLoadedVersion '1.1.3'
}
dependencies {
bootstrap "org.grails.plugins:tomcat:7.0.50" // No container is deployed by default, so add this
compile 'org.grails.plugins:resources:1.2' // Just an example of adding a Grails plugin
}
project.tasks.addRule('Pattern: grails-test-app-<phase>') { String taskName ->
println tasks //shows grails-test-app-xxxxx task. Why?
//if (taskName.startsWith('grails-test-app') && taskName != 'grails-test-app') {
// task(taskName, type: GrailsTestTask) {
// String testPhase = (taskName - 'grails-test-app').toLowerCase()
// phases = [testPhase]
// }
//}
}
Running $gradle grails-test-integration
or in fact anything of the form $gradle grails-test-app-xxxxxxxx yields the error "Cannot add task 'gradle grails-test-app-xxxxxxxx as a task with that name already exists".
Can someone please advise how I can resolve this error? Thanks.

If you don't mind overriding the task created by the plugin, you might want to try
task(taskName, type: GrailsTestTask, overwrite: true)
In general, when using task rules that can be called multiple times (for instance if you have multiple tasks depending on a task eventually added by your rules), I use the following test before actually creating the task:
if (tasks.findByPath(taskName) == null) {tasks.create(taskName)}
This will call the task() constructor only if this task name does not exists.

Related

How to lazily get main sourceSets in Gradle

I need to configure sources jar for all of my subprojects. I have the following subprojects configuration defined:
subprojects {
apply(plugin = "java-library")
val sourcesJar = tasks.registering(Jar::class) {
archiveClassifier.set("sources")
from(the<SourceSetContainer>().named("main").get().allJava)
}
tasks.named("assemble") {
dependsOn(sourcesJar)
}
}
When I try to run ./gradlew tasks I receive an exception in my subproject saying that:
Extension of type 'SourceSetContainer' does not exist. Currently registered extension types: [ExtraPropertiesExtension]
My assumption is that the access to get() method of the extension causes problems but without that I cannot refer to the allJava sources that I need. So how to achieve desired result by using configuration avoidance API?
Running on Gradle 5.2.1 with Kotlin DSL.
I found working solution. The problem is that when you call the<T>() function it is taken from the Task type which is why it complains about absent extension. The solution is to call the<T>() function on project instance like this:
subprojects {
apply {
plugin<JavaLibraryPlugin>()
}
val sourcesJar by tasks.registering(Jar::class) {
archiveClassifier.set("sources")
from(
// take extension from project instance
project.the<SourceSetContainer>().named("main").get().allJava
)
}
}

I got an error using this build.grafle file and don't know how to fix it

Here's the Error:
FAILURE: Build failed with an exception.
Where: Build file '/home/wieland/GitGradlePackaging/build.gradle' line: 22
What went wrong: A problem occurred evaluating root project 'GitGradlePackaging'.
Could not get unknown property 'org' for object of type org.gradle.api.internal.initialization.DefaultScriptHandler.
And Here's 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 guide available at https://docs.gradle.org/4.6/userguide/tutorial_java_projects.html
*/
//From example: http://mrhaki.blogspot.co.at/2015/04/gradle-goodness-use-git-commit-id-in.html
buildscript {
repositories {
jcenter()
}
dependencies {
//Add dependencies for build script, so we can access Git from our build script
classpath 'org.ajoberstar:grgit:1.1.0'
}
def git = org.ajoberstar.grgit.Grgit.open(file('.'))
//To save Githash
def githash = git.head().abbreviatedId
}
plugins {
// Apply the java plugin to add support for Java
id 'java'
// Apply the application plugin to add support for building an application
id 'application'
// Apply the groovy plugin to also add support for Groovy (needed for Spock)
id 'groovy'
id 'distribution'
}
// Set version
project.version = mainProjectVersion + " - " + githash
project.ext.set("wholeVersion", "$project.version - $githash")
project.ext.set("buildtimestamp", "2000-01-01 00:00")
def versionfilename = "versioninfo.txt"
def GROUP_DEBUG = 'Debug'
// Task to print project infos
task debugInitialSettings {
group = GROUP_DEBUG
doLast {
println 'Version: ' + project.wholeVersion
println 'Timestamp: ' + project.buildtimestamp
println 'Filename: ' + project.name
}
}
// To add the githash to zip
task renameZip {
doLast {
new File ("$buildDir/distributions/$project.name-${project.version}.zip")
.renameTo ("$buildDir/distributions/$project.name-${project.wholeVersion}.zip")
}
}
distZip.finalizedBy renameZip
// To add the githash to tar
task renameTar{
doLast {
new File ("$buildDir/distributions/$project.name-${project.version}.tar")
.renameTo ("$buildDir/distributions/$project.name-${project.wholeVersion}.tar")
}
}
distTar.finalizedBy renameTar
// Define the main class for the application
mainClassName = 'App'
dependencies {
// This dependency is found on compile classpath of this component and consumers.
compile 'com.google.guava:guava:23.0'
// Use the latest Groovy version for Spock testing
testCompile 'org.codehaus.groovy:groovy-all:2.4.13'
// Use the awesome Spock testing and specification framework even with Java
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile 'junit:junit:4.12'
}
// 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()
}
//To generate Testreports as HTML
test {
reports {
junitXml.enabled = false
html.enabled = true
}
}
distributions {
main {
contents {
from { 'build/docs' }
into ('reports') {
from 'build/reports'
}
}
}
}
//To make sure that test and javadoc ran before zip and tar
distTar.dependsOn test
distZip.dependsOn test
distTar.dependsOn javadoc
distZip.dependsOn javadoc
Please keep in mind I have not much knowledge about gradle as I'm just starting to learn it!
Thanks in advance :)
You have to move the githash definition outside the buildscript block
buildscript {
repositories {
jcenter()
}
dependencies {
//Add dependencies for build script, so we can access Git from our build script
classpath 'org.ajoberstar:grgit:1.1.0'
}
}
def git = org.ajoberstar.grgit.Grgit.open(file('.'))
//To save Githash
def githash = git.head().abbreviatedId
The reason is that when the buildscript block is evaluated line by line, its dependencies are not yet loaded. When the rest of the script is evaluated, the dependencies of the buildscript block have already been loaded. This is actually the reason for the buildscript block existence: to be run before the rest of the build and prepare the setup.

classes not visible to gradle task swagger when using kongchen swagger-maven-plugin

When performing gradle clean and then gradle swagger a ClassNotFoundException is thrown. If gradle swagger is then run again (basically after the api build is done in previous run), it works fine.
build.gradle looks as below:
buildscript {
repositories {
maven { url hydraMavenRepo }
maven { url hydraPluginsRepo }
}
dependencies {
classpath "com.github.kongchen:swagger-maven-plugin:3.1.4"
}
}
apply plugin: 'java'
configurations {
addclasspath
}
dependencies {
addclasspath files(project(':api:desktop-api').configurations['runtime'].files)
addclasspath files(project(':api:desktop-api').sourceSets['main'].output)
addclasspath files(project(':api:desktop-api').sourceSets.main.output.classesDir)
runtime project(':api:desktop-api')
}
sourceSets {
main {
runtimeClasspath += files(project(':api:desktop-api').sourceSets['main'].output)
runtimeClasspath += files(project(':api:desktop-api').sourceSets['main'].output.classesDir)
runtimeClasspath += files(project(':api:desktop-api').configurations['runtime'].files)
}
}
import com.github.kongchen.swagger.docgen.mavenplugin.ApiDocumentMojo
import com.github.kongchen.swagger.docgen.mavenplugin.ApiSource
import io.swagger.models.Info
task swagger(dependsOn: [':api:desktop-api:build']) {
doLast {
logger.info 'Swagger GenDoc...'
project.file(reportsDir).mkdirs()
// a trick to have all needed classes in the classpath
def customClassLoader = new GroovyClassLoader()
buildscript.configurations.classpath.each {
//println it.toURI().toURL()
customClassLoader.addURL(it.toURI().toURL())
}
configurations.addclasspath.each {
customClassLoader.addURL(it.toURI().toURL())
}
// the same settings as in the swagger-maven-example/pom.xml
final ApiDocumentMojo mavenTask = Class.forName('com.github.kongchen.swagger.docgen.mavenplugin.ApiDocumentMojo', true, customClassLoader).newInstance(
apiSources: [
new ApiSource(
springmvc: false,
locations: ['com/vmware/vdi/hydra'],
schemes: ['http', 'https'],
host: 'vmware.com',
basePath: '/api',
info: new Info(
title: "Hydra DS-REST API's",
version: 'v100',
description: "Hydra DS-REST API's",
),
swaggerDirectory: reportsDir
)
]
)
mavenTask.execute()
logger.info 'Swagger GenDoc task is completed'
}
}
You have several flaws in your build script.
You should not depend on built things in build script dependencies. This is a hen and egg problem. You need to execute the build to get the classes that are necessary to execute the build. This cannot work reliably, if at all.
Instead you should declare them as dependencies outside the buildscript block. The buildscript block is only for dependencies that are needed by your build script to run itself, like Gradle Tasks and Gradle Plugins or classes you use during the build, like the swagger-maven-plugin stuff which is correct in the buildscript block.
Besides that, you execute part of your swagger stuff (the instanciation, execution and printing) during the configuration phase instead of during the execution phase. Everything you do in a task closure, but outside any doFirst or doLast blocks is run during the configuration phase, when the build is configured and thus always, no matter what tasks you actually want to execute and no matter whether the task may already be up-to-date or not. For the up-to-date check to work and save your time you need to declare all inputs like files and properties that might change between executions and all outputs that you generate, then Gradle can do its magic to only execute the task when actually necessary.
Also you should not use println in build scripts. That is like using System.out.println in Java programs. Instead you should use the provided logging facility directly, e. g. doing logger.info 'Swagger GenDoc task is completed'.
buildscript.classloader is what I was looking for.
Below is the code that works:
buildscript {
repositories {
maven { url mavenRepo }
}
dependencies {
classpath "com.github.kongchen:swagger-maven-plugin:3.1.4"
}
}
import com.github.kongchen.swagger.docgen.mavenplugin.ApiDocumentMojo
import com.github.kongchen.swagger.docgen.mavenplugin.ApiSource
import io.swagger.models.Info
task swagger(dependsOn: ':api:build') {
doLast {
logger.info 'Swagger GenDoc...'
project.file(<dir>).mkdirs()
FileCollection apiRuntimeFiles = files(project(':api').configurations['runtime'].files)
apiRuntimeFiles.each {
buildscript.classLoader.addURL(it.toURI().toURL())
}
FileCollection apiClassFiles =files(project(':api').sourceSets['main'].output)
apiClassFiles.each {
buildscript.classLoader.addURL(it.toURI().toURL())
}
final ApiDocumentMojo mavenTask = Class.forName('com.github.kongchen.swagger.docgen.mavenplugin.ApiDocumentMojo', true, buildscript.classLoader).newInstance(
apiSources: [
new ApiSource(
springmvc: false,
locations: ['<loc>'],
schemes: ['http', 'https'],
host: '<host>',
basePath: '/api',
info: new Info(
title: "REST API's",
version: 'v1',
description: "REST API's",
),
swaggerDirectory: <dir>
)
]
)
mavenTask.execute()
logger.info 'Swagger GenDoc task is completed'
}
}

whenReady not setting gradle property before task runs

I have a a version property in my gradle.properties file that gives the version I am building. I have a task in the build called release that if present in the task graph will upload to the snapshot repo. However what is happening is that even though I include the release task in the build tasks, snapshot is not appended to my version property when uploadArchives runs so it attempts to upload to the wrong repository and fails. The when ready runs, but it does not seem to run before uploadArchives. Can anyone explain what is happening here?
uploadArchives {
repositories {
ivy {
credentials {
username nexusUser
password nexusPassword
}
if (version.endsWith("-SNAPSHOT")) {
url nexusSnapshotRepository
} else {
url nexusReleaseRepository
}
}
}
}
gradle.taskGraph.whenReady {taskGraph ->
if (!taskGraph.hasTask(release)) {
version = version + '-SNAPSHOT'
}
println "release task not included - version set to $version"
}
task release(){
doLast{
println "Releasing"
}
}
This is very similar to the example on the gradle site so I don't see what is going wrong.
http://www.gradle.org/docs/current/userguide/tutorial_using_tasks.html
The script is checking the project.version value in the configuration phase (not when the task executes), but only modifying it after the task execution graph has been built. One way to fix this is to override the repository url from inside the taskGraph.whenReady callback:
uploadArchives {
repositories {
ivy {
name "nexus"
url nexusReleaseRepository
...
}
}
}
gradle.taskGraph.whenReady { taskGraph ->
if (!taskGraph.hasTask(release)) {
version += '-SNAPSHOT'
uploadArchives.repositories.nexus.url nexusSnapshotRepository
// ps: println has to go inside here
}
}

how to download external files in gradle?

I have a gradle project which requires some data files available somewhere on the internet using http. The goal is that this immutable remote file is pulled once upon first build. Subsequent build should not download again.
How can I instruct gradle to fetch the given file to a local directory?
I've tried
task fetch(type:Copy) {
from 'http://<myurl>'
into 'data'
}
but it seems that copy task type cannot deal with http.
Bonus question: is there a way to resume a previously aborted/interrupted download just like wget -c does?
How about just:
def f = new File('the file path')
if (!f.exists()) {
new URL('the url').withInputStream{ i -> f.withOutputStream{ it << i }}
}
You could probably use the Ant task Get for this. I believe this Ant task does not support resuming a download.
In order to do so, you can create a custom task with name MyDownload. That can be any class name basically. This custom task defines inputs and outputs that determine whether the task need to be executed. For example if the file was already downloaded to the specified directory then the task is marked UP-TO-DATE. Internally, this custom task uses the Ant task Get via the built-in AntBuilder.
With this custom task in place, you can create a new enhanced task of type MyDownload (your custom task class). This task set the input and output properties. If you want this task to be executed, hook it up to the task you usually run via task dependencies (dependsOn method). The following code snippet should give you the idea:
task downloadSomething(type: MyDownload) {
sourceUrl = 'http://www.someurl.com/my.zip'
target = new File('data')
}
someOtherTask.dependsOn downloadSomething
class MyDownload extends DefaultTask {
#Input
String sourceUrl
#OutputFile
File target
#TaskAction
void download() {
ant.get(src: sourceUrl, dest: target)
}
}
Try like that:
plugins {
id "de.undercouch.download" version "1.2"
}
apply plugin: 'java'
apply plugin: 'de.undercouch.download'
import de.undercouch.gradle.tasks.download.Download
task downloadFile(type: Download) {
src 'http://localhost:8081/example/test-jar-test_1.jar'
dest 'localDir'
}
You can check more here: https://github.com/michel-kraemer/gradle-download-task
For me works fine..
The suggestion in Ben Manes's comment has the advantage that it can take advantage of maven coordinates and maven dependency resolution. For example, for downloading a Derby jar:
Define a new configuration:
configurations {
derby
}
In the dependencies section, add a line for the custom configuration
dependencies {
derby "org.apache.derby:derby:10.12.1.1"
}
Then you can add a task which will pull down the right files when needed (while taking advantage of the maven cache):
task deployDependencies() << {
String derbyDir = "${some.dir}/derby"
new File(derbyDir).mkdirs();
configurations.derby.resolve().each { file ->
//Copy the file to the desired location
copy {
from file
into derbyDir
// Strip off version numbers
rename '(.+)-[\\.0-9]+\\.(.+)', '$1.$2'
}
}
}
(I learned this from https://jiraaya.wordpress.com/2014/06/05/download-non-jar-dependency-in-gradle/).
Using following plugin:
plugins {
id "de.undercouch.download" version "3.4.3"
}
For a task which has the purpose of only downloading
task downloadFile(type: Download) {
src DownloadURL
dest destDir
}
For including download option into your task:
download {
src DownloadURL
dest destDir
}
For including download option with multiple downloads into your task:
task downloadFromURLs(){
download {
src ([
DownloadURL1,
DownloadURL2,
DownloadURL3
])
dest destDir
}
}
Hope it helped :)
just now ran into post on upcoming download task on gradle forum.
Looks like the perfect solution to me.. Not (yet) available in an official gradle release though
Kotlin Version of #Benjamin's Answer
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath("com.android.tools.build:gradle:4.0.1")
}
}
tasks.register("downloadPdf"){
val path = "myfile.pdf"
val sourceUrl = "https://file-examples-com.github.io/uploads/2017/10/file-sample_150kB.pdf"
download(sourceUrl,path)
}
fun download(url : String, path : String){
val destFile = File(path)
ant.invokeMethod("get", mapOf("src" to url, "dest" to destFile))
}

Resources