Gradle shortcut notations does not work for copy task - gradle

I'm new to gradle and i tried to copy files from one folder to another using a task of "Copy" type, but it does not work. The following is my script:
def dest = 'newfolder'
task copy(type: Copy) << {
println dest
from "src"
into dest
}
But if i remove the "<<" and run gradle copy again, it works. Script like following:
def dest = 'newfolder'
task copy(type: Copy) {
println dest
from "src"
into dest
}
Why doesn't it work if i use "<<" ?
But when i run another following scripts, they all work.
task hello << {
println 'Hello world!'
}
task hello {
println 'Hello world!'
}

<< (short for doLast) adds a task action, which will be executed after the Copy task's main task action (which does the copying). At that point it's too late to configure the task, as the main action has already completed.

Related

Incremental build support for output directory considering added files

The Gradle documentation tells this:
Note that if a task has an output directory specified, any files added to that directory since the last time it was executed are ignored and will NOT cause the task to be out of date. This is so unrelated tasks may share an output directory without interfering with each other. If this is not the behaviour you want for some reason, consider using TaskOutputs.upToDateWhen(groovy.lang.Closure)
Question: How does the solution with upToDateWhen look like (so that added files are considered). The main problem is that one has to access the build cache to retrieve the output directory content hash the last time the task ran.
Not sure if I understand the question correctly or why you mention the build cache. I assume you are not aware that the predicates added with upToDateWhen() are considered in addition to any other up-to-date checks like the ones added with TaskOutputs.dir()?
Take the following sample task:
task foo {
def outDir = file('out')
outputs.dir(outDir)
outputs.upToDateWhen { outDir.listFiles().length == 1 }
doLast {
new File(outDir, 'foo.txt') << 'whatever'
}
}
As long as there is only a single file in the output directory (as configured via upToDateWhen) and the file produced by the task (out/foo.txt) isn’t changed after the task has run, the task will be up-to-date. If you change/remove the file created by the task in the output directory or if you add further files to the output directory, then the task will run again.
Updated answer as per the updated question from the comments:
task foo {
def outDir = file('out')
/* sample task action: */
doFirst {
def numOutFiles = new Random().nextInt(5)
for (int i = 1; i <= numOutFiles; i++) {
new File(outDir, "foo${i}.txt") << 'whatever'
}
}
/* up-to-date checking configuration: */
def counterFile = new File(buildDir, 'counterFile.txt')
outputs.dir(outDir)
outputs.upToDateWhen {
counterFile.isFile() \
&& counterFile.text as Integer == countFiles(outDir)
}
doLast {
counterFile.text = countFiles(outDir)
}
}
def countFiles(def dir) {
def result = 0
def files = dir.listFiles()
if (files != null) {
files.each {
result++
if (it.isDirectory()) {
result += countFiles(it)
}
}
}
result
}

execute bash command in a gradle function

I want to create a generic function in gradle that executes a command. This function is called from a task.
The function executeCommand is triggered from the task copyFile but it seems that the commandLine commands are not executed. I did this because I need a generic ececuteCommand functionality that is triggered from multiple jobs.
def executeCommand(execCmd) {
try {
exec {
println("execute $execCmd in .")
commandLine 'bash', '-c', "ls -la"
commandLine 'bash', '-c', "${execCmd}"
}
}
catch(Exception e){
println("Exception: $e")
}
}
task copyFile {
doLast {
if(project.hasProperty('file')) {
ext.myFile = file
def execCmd="cp ${myFile} ."
executeCommand(${execCmd})
}
else {
println("Please specifiy argument files -Pfile=SRC_PATH")
}
}
}
There is a syntax error in your script, you should normally have an error as follows during execution:
* What went wrong:
Execution failed for task ':copyFile'.
> Could not find method $() for arguments [build_djiuilz6w3giaud8hgmf0oze7$_run_closure2$_closure5$_closure6#57fdda61] on task ':copyFile' of type org.gradle.api.DefaultTask. (normally you should have an error when trying to execute it : **
you need to replace the following statement in your copyFile.doLast{ } block:
executeCommand(${execCmd})
with:
executeCommand( execCmd)
// or: executeCommand( "${execCmd}" )
NOTE: in the exec {} block of your executeCommand function, there are two calls to commandLine function: only the second one will have effect so the command 'ls -al' will never be executed.
The rest of your script seems valid and should work as expected.

gradle - create gzip for each file in a FileTree

I want to create multiple gzip file for each txt file in a FileTree. Here is my gradle task:
task gzipFiles << {
FileTree tree = fileTree('build/dist') {
include '**/*.txt'
}
tree.each {File file ->
println file
String fileName = file.getName()
//create gzip file
}
}
Is there anyway to use Tar task inside FileTree each loop to create a gzip?
No, it's impossible - it will not work that way. What you can do is to invert the processing, here you go:
task all
fileTree('input') {
include('**/*.txt')
}.each { f ->
task "tar_$f.name"(type: Tar) { t ->
from f.parentFile
include f.name
baseName = "$f.name"
destinationDir = project.file('output')
all.dependsOn(t)
}
}
Here is the demo.

Call one Gradle Task from another with arguments

I have a gradle task which calls a script and passed the command line arguments to the script using -PARGS.
task taskAll(type: Exec, dependsOn: taskinit) {
environment['PROJECT_ROOT'] = "${projectDir}"
workingDir rootProject.projectDir.path
description = 'Main task'
executable rootProject.projectDir.path + "/execute.me"
if (project.hasProperty('ARGS')) {
args(ARGS.split(','))
}
}
I call this gradle task with any of the below options
./gradlew taskAll
./gradlew taskAll -PARGS="arg1"
./gradlew taskAll -PARGS="arg2"
However, am looking to see if I split taskAll into multiple tasks, say
./gradlew taskA #Calls task taskAll with arg1
./gradlew taskB #Calls task taskAll with arg2
I understand that I will have to replicate the taskAll to create taskA, taskB and remove the "if" condition and hardcode args in each of these.
However, I wonder if it is possible to have a cleaner implementation by having MainTask which only calls the executable, and then have TaskA, TaskB, TaskC call MainTask and pass the arguments arg1, arg2 and arg3.
In most cases, executing one task from another is done by configuration of task dependencies, via providing dependsOn and optionally mustRunAfter properies. In your case, it's not possible to use it, since your main task has to be executed after some configuration task. In that case, you can use finalizedBy property of the task.
For your requirements, you can create a number of tasks, which will set some script variable with predefined arguments, just as you need it. And you could leave your main task, which will call something, relying on this arguments. Only thing you need to do, is to make each custom task finilizedBy your main task. So, every call of custom task will execute the main task after excution.
Here is the short example, how to do it:
//define a variable to store arguments
def ARGS = null
//2 custom tasks, which set arguments during the execution phase
task taskA << {
ARGS = "poperty1,property2"
}
task taskB << {
ARGS = "property3,property4"
}
//your main task
task mainTask (type: Exec) {
environment['PROJECT_ROOT'] = "${projectDir}"
workingDir rootProject.projectDir.path
description = 'Main task'
executable rootProject.projectDir.path + "/execute.me"
//here is the main difference, we moved arguments setting into
//execution phase, before execution of this task
doFirst{
//if you call custom task it will be executed with predefined params
if (ARGS != null) {
args(ARGS)
//if you call mainTask, you are able to pass arguments via command line with -PCOMMAND_LINE_ARGS=123
} else if (project.hasProperty('COMMAND_LINE_ARGS')) {
args(COMMAND_LINE_ARGS)
} else {
throw new GradleException("No arguments found")
}
}
}
//finilization settings for custom tasks
taskA.finalizedBy mainTask
taskB.finalizedBy mainTask
A prettier way to do that with GradleBuild API:
task ('taskA', type: GradleBuild) {
startParameter.projectProperties = ['ARGS':'arg1']
tasks = ['taskAll']
}
task ('taskB', type: GradleBuild) {
startParameter.projectProperties = ['ARGS':'arg2']
tasks = ['taskAll']
}
You can have complex project properties, for example command line argument -Pmyextension.config=true will become :
startParameter.projectProperties = ['myextension.config':true]
Note that this will erase CLI args. If you need to append it :
startParameter.projectProperties << project.getGradle().getStartParameter().getProjectProperties() << ['myextension.config':true]
You can use ext:
task outraTask << {
printf(arg0)
printf(arg1)
}
project(':projetc2').tasks.outraTask {
ext.arg0 = "0"
ext.arg1 = "1"
}.execute()
output:
> Task :projetc2:outraTask
0
1

How do I concatenate multiple files in Gradle?

Is there an easy way to concatenate multiple text files into a single one in Gradle? The build script should look something like this:
FileCollection jsDeps = files(
'file1.js',
'file2.js'
// other files here
)
task concatenate << {
// concatenate the files to file.js
}
I am using Gradle 2.3.
leftShift / "<<" is deprecated in gradle 3.4 You may use something like:
task concatenate {
doLast {
def toConcatenate = files("filename1", "filename2", ...)
def outputFileName = "output.txt"
def output = new File(outputFileName)
output.write('') // truncate output if needed
toConcatenate.each { f -> output << f.text }
}
You can also register the files as inputs/outputs to help with incremental builds. It's especially helpful with larger files.
something like this:
task 'concatenateFiles', {
inputs.files( fileTree( "path/to/dir/with/files" ) ).skipWhenEmpty()
outputs.file( "$project.buildDir/tmp/concatinated.js" )
doLast {
outputs.files.singleFile.withOutputStream { out ->
for ( file in inputs.files ) file.withInputStream { out << it << '\n' }
}
}
}
Instead of the fileTree, it can also be replaced with sourceset/sourceset output, specific files, outputs from a different task, etc.
Gradle doc on task inputs/output
Concatenating files in groovy
The following task should do the job:
task concatenate << {
def toConcatenate = files('f1', 'f2', 'f3')
def output = new File('output')
toConcatenate.each { f -> output << f.text }
}
(new File('test.js')).text = file('test1.js').getText() + file('test2.js').getText()
UPDATE:
For collections.
(new File('test.js')).text = files('test1.js', 'test2.js').collect{it.getText()}.join("\n")

Resources