Gradle - publish artifacts - gradle

I want to publish artifacts to ivy repository but it doesn't work. I read this article and after read created this sample build:
task ivyPublishTest << {
def buildDir = new File("build")
buildDir.mkdirs()
def fileToPublish = new File("build/file.abc")
fileToPublish.write("asdasdasd")
}
artifacts {
archives(ivyPublishTest.fileToPublish) {
name 'gradle-test-artifact'
builtBy ivyPublishTest
}
}
uploadArchives {
repositories {
ivy {
url "http://my.ivy.repo/ivyrep/shared"
}
}
}
Of course the problem is that it doesn't work. I get this error Could not find property 'fileToPublish' on task ':ivyPublishTest'

In Groovy, def creates a local variable, which is lexically scoped. Therefore, fileToPublish is not visible outside the task action. Additionally, configuration has to be done in the configuration phase (i.e. the declaration and assignment of fileToPublish in your task action would come too late). Here is a correct solution:
task ivyPublishTest {
// configuration (always evaluated)
def buildDir = new File("build")
ext.fileToPublish = new File("build/file.abc")
doLast {
// execution (only evaluated if and when the task executes)
buildDir.mkdirs()
fileToPublish.write("asdasdasd")
}
}
artifacts {
// configuration (always evaluated)
archives(ivyPublishTest.fileToPublish) {
name 'gradle-test-artifact'
builtBy ivyPublishTest
}
}
ext.fileToPublish = ... declares an extra property, a new property attached to an existing object that's visible everywhere the object (task in this case) is visible. You can read more about extra properties here in the Gradle User Guide.

Related

Refactor duplicated code in gradle task "type: Copy"

A gradle project contains several similar task type:Copy and some of them require additional checks.
task copyPackage1(type: Copy) {
from buildPackage1
into "/pkgs/"
eachFile {
if (it.relativePath.getFile(destinationDir).exists()) {
throw new GradleException("Probably version was no updated. File exists: " + it)
}
}
}
...
task copyPackage2(type: Copy) {
from buildPackage2
into "/pkgs/"
eachFile {
if (it.relativePath.getFile(destinationDir).exists()) {
throw new GradleException("Probably version was no updated. File exists: " + it)
}
}
}
How it is possible to refactor duplicated checks and specify same target directory for all similar tasks (but not all Copy tasks)?
You could either implement this with a custom Gradle plugin (as suggested in this similar question on Gradle forum), or use simple Groovy method to create and configure your tasks, as follows:
// define a "task creator" method
ext.createCopyToPkgTask = { String taskName , String fromDir ->
return project.tasks.create(taskName, Copy.class){
from fromDir
into "/pkgs"
eachFile {
if (it.relativePath.getFile(destinationDir).exists()) {
throw new GradleException("Probably version was no updated. File exists: " + it)
}
}
}
}
/* declare your similar tasks using the creator method above */
createCopyToPkgTask("copyPackage1","buildPackage1")
createCopyToPkgTask("copyPackage2","buildPackage2")
// ...

refer to Gradle task which is defined later in build

Our build has some generic logic to create tasks from configuration data. There are also several Gradle files on whose execution order I do not want to depend.
Now I need to add a task dependency to a task without knowing if that task will be defined before my part of the build script runs (at configuration time) or after my part.
[Edit: as #canillas writes I can say myTask.dependsOn "otherTask" and "lazily" depend on yet undefined "otherTask", but I need it the other way around.]
Since I can't write tasks["otherTask"].dependsOn myNewTask before "otherTask" is defined, I made the following helper function:
void safelyDoWithTask(String taskName, Closure func) {
def task = tasks.findByName(taskName)
if (task != null) {
func(task)
} else {
project.tasks.whenTaskAdded { task_ ->
if (taskName.equals(task_.getName())) {
func(task_)
}
}
}
}
safelyDoWithTask('otherTask', { it.dependsOn myNewTask })
Now I wonder if there is more canonical way to achieve this?
Consider the following:
// section 1
def dynamicDependencies = [:]
dynamicDependencies['otherTask'] = 'myNewTask'
// section 2
afterEvaluate { project ->
// taskA dependsOn taskB
dynamicDependencies.each { taskA, taskB ->
def task = project.tasks.findByName(taskA)
if (task) { task.dependsOn "${taskB}" }
}
}
// section 3
task myNewTask() {
doLast {
println 'myNewTask !'
}
}
task otherTask() {
doLast {
println 'otherTask !'
}
}
The gist is:
section 1 defines our dependency info in a custom map
section 2 uses the afterEvaluate hook to process the map
because the above is decoupled from the task definitions, we can simply put them in section 3 (or wherever)

Skip variable checking in gradle configuration phase

I have some gradle script where i read some properties file (differen file in different execution configurations) and assign Properties obect to "ext" property in each task.
task provisionMongoDBCopyDockerfile(type: Copy, dependsOn: 'readTestConfiguration') {
from "${projectDir}/deployment/scripts/Dockerfile.mongodb"
into "/tmp/stand/mondodb"
expand(ext.stand)
filteringCharset = 'UTF-8'
}
task readTestConfiguration () {
def props = loadStandProperties('test')
println props
tasks.each {
it.ext.stand = props
println it.ext
}
}
but when i run gradle script i get this error: "Cannot get property 'stand' on extra properties extension as it does not exist" in line with "expand(ext.stand)". How can i solve this problem. I don't want to put all configuration parameters in "gradle.properties" and change it from configuration to configuration.
Consider the following (using Gradle 2.14.1). This effectively sets up a dependency in Configuration phase. Also it uses project.ext versus tasks.ext.
def readTestConfiguration = {
def props = loadStandProperties('test')
println props
project.ext.stand = props
}
def loadStandProperties (def env) {
// use mock data
return ["foo": "bar"]
}
tasks.whenTaskAdded { Task task ->
if (task.name == "provisionMongoDBCopyDockerfile") {
readTestConfiguration()
}
}
task provisionMongoDBCopyDockerfile(type: Copy) {
from "${projectDir}/in"
into "${projectDir}/out"
expand(project.ext.stand)
}

How to use a parameter in gradle copy task in the destination folder?

Given the following task in gradle, the dev would start a new module by:
gradle initModule -PmoduleName=derp
task initModule(type: Copy) {
description "Initialize an empty module based on the template. Usage: gradle initModule -P moduleName=derp"
onlyIf { project.hasProperty("moduleName") }
from "$rootDir/modules/template"
into "$rootDir/modules/$moduleName"
}
I am unable to run gradle since moduleName is not defined during evaluation, although I was hoping that "onlyIf" would do so.
To solve this I assign it to a locally defined variable in a guard block:
def modName = ""
if (!project.hasProperty("moduleName")) {
logger.error("Invalid moduleName : It can't be")
} else {
modName = moduleName
}
and finally use the new variable to survive the configuration phase.
Is this the right way to do this? It just doesn't feel right.
Additionally if I was using a rule to make this a tad more elegant:
tasks.addRule("Pattern: initModule_<mod name>") { String taskName ->
if (taskName.startsWith("initModule_")) {
def params = taskName.split('_');
task(taskName) {
doLast {
project.ext.moduleName = params.tail().head()
tasks.initModule.execute()
}
}
}
}
The moduleName is not passed around (even if I change it to finalizedBy).
Any help is appreciated.
As you already figured out the property is not available during the configuration phase.
But can postpone the look-up to the execution phase by using
into "$rootDir/modules/" + project.property("moduleName")
instead of
into "$rootDir/modules/$moduleName"

Grails gradle "a task with that name already exists"

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.

Resources