gradle tar task not executed - gradle

I am writing a Gradle build for a non-java project for assembling existing directories and tar archives into a .tar.gz The tar task skips if I use the definition like so:
task archive(dependsOn: 'initArchive',type: Tar) << {
baseName = project.Name
destinationDir = new File(project.buildDir.path+'/installer')
compression = Compression.GZIP
from (archiveDir)
doLast{
checksum(archivePath)
}
}
here's the console output
:jenkins-maven-sonar:archive
Skipping task ':jenkins-maven-sonar:archive' as it has no source files.
:jenkins-maven-sonar:archive UP-TO-DATE
BUILD SUCCESSFUL
Total time: 9.056 secs
When I try to use tar task as a method it fails complaining cannot find method
task archive(dependsOn: 'initArchive') << {
tar{
baseName = project.Name
destinationDir = new File(project.buildDir.path+'/installer')
compression = Compression.GZIP
from (archiveDir)
doLast{
checksum(archivePath)
}
}
}
FAILURE: Build failed with an exception.
* Where:
Build file '/home/anadi/Code/da-ci-installers/build.gradle' line: 29
* What went wrong:
Execution failed for task ':jenkins-maven-sonar:archive'.
> Could not find method tar() for arguments [build_6a2bckppv2tk8qodr6lkg5tqft$_run_closure3_closure5_closure7#4a5f634c] on task ':jenkins-maven-sonar:archive'.
* Try:
Run with --stacktrace option to get the stack trace. Run with --debug option to get more log output.
BUILD FAILED
Total time: 8.749 secs
Can we run the tar task in same way as Gradle allows running copy? In the same build I have a block like follows and I want to know if tar can used in the same way
copy {
project.logger.info("Copying bundle :: "+bundle[x])
from(rootProject.projectDir.path+"/3rd-party-tools/"+bundle[x]) {
include '**/*.*'
}
into(archiveDir)
}
if not how to make sure my build does not "skip tar" task if using the first form described above.

You have fallen for the classical mistake of configuring a task in the execution phase rather than the configuration phase. The solution is to remove the << in the first code snippet.
If you find the << (and the difference it makes) confusing, a good solution is to never use << but always the more explicit doLast {}.
There is no tar method, but it's usually better to make these things a separate task anyway. (Methods like copy should only be preferred over the corresponding task if there is a strong reason.)

I had a funny situation where I got hit by this when using doLast{} on a tar task.
It was because of a multi-project build:
build.gradle
--> sub-project
--> build.gradle
In this case if you try to have a tar or a copy task in the main build file that references something from that project(":sub-project") uses it will tempt the developer to wrap it in doLast.
For example, main build.gradle file has:
task distTar(type: Tar, dependsOn: "buildDist") {
description "Package ProjName into a Tar file"
group "Build"
baseName = 'outbasename'
archiveName = baseName + '.tar.gz'
compression = Compression.GZIP
destinationDir = file(project(":packaging").buildDir.path)
extension = 'tar.gz'
into('outdir') {
from project(":sub-project").war
}
}
So they got an error that project(":sub-project").war doesn't exist. So to get around it someone put doLast {} the task and errors went away. BAD!!
task distTar(type: Tar, dependsOn: "buildDist") {
doLast { // <-- BAD!!
description "Package ProjName into a Tar file"
group "Build"
baseName = 'outbasename'
archiveName = baseName + '.tar.gz'
compression = Compression.GZIP
destinationDir = file(project(":packaging").buildDir.path)
extension = 'tar.gz'
into('outdir') {
from project(":sub-project").war
}
}
}
Then i was left to fix it. So the correct was to do it was to add
evaluationDependsOn ":sub-project"
In the main build.gradle file. Now it knows to evaluate it. I remove the incorrect doLast{} block and now the task is no longer ignored.

Related

Setting file permissions dynamically in a gradle Copy task

I'm trying to get around a problem where a dependency in my build is a zip file that contains some read only files. When I extract that zip as part of my build I end out with read only files in a staging folder and they prevent the task running in the future since they cannot be overwritten.
Until there's a way to force overwrite in a gradle copy task I've been trying to find a way to change the file mode of the read-only files in a way that doesn't remove the execute bit from those files that need it.
I've come up with this:
task stageZip(type: Copy) {
from({ zipTree(zipFile) })
into stagingFolder
eachFile {
println "${it.name}, oldMode: ${Integer.toOctalString(it.mode)}, newMode: ${Integer.toOctalString(it.mode | 0200)}"
fileMode it.mode | 0200
}
}
But this doesn't work. If I comment out the fileMode line then the println correctly lists the old and new file modes with the write bit enabled for all files. If I leave the code as is, then all the files in the zip get extracted with the newMode of the first file.
This doesn't seem like this is an unreasonable thing to try and do, but I'm obviously doing something wrong. Any suggestions?
Based on this thread, consider the Sync task. Specifically:
task stageZip(type: Sync) {
from zipTree('data/data.zip')
into 'staging'
fileMode 0644
}
I've put a working example (as I understand the question) here.
Here is a method that answers the question about file permissions. The example is posted to GitHub here.
First, consider a method to add w to a file:
import java.nio.file.*
import java.nio.file.attribute.PosixFilePermission
def addWritePerm = { file ->
println "TRACER adding 'w' to : " + file.absolutePath
def path = Paths.get(file.absolutePath)
def perms = Files.getPosixFilePermissions(path)
perms << PosixFilePermission.OWNER_WRITE
Files.setPosixFilePermissions(path, perms)
}
then, the Gradle task could be as follows:
project.ext.stagingFolder = 'staging'
project.ext.zipFile = 'data/data.zip'
task stageZip(type: Copy) {
from({ zipTree(project.ext.zipFile) })
into project.ext.stagingFolder
doLast {
new File(project.ext.stagingFolder).eachFileRecurse { def file ->
if (! file.canWrite()) {
addWritePerm(file)
}
}
}
}
eachFile {
file -> file.setMode(file.getMode() | 0200)
}
Worked for me in an rpm task which works with copyspec

Gradle configure task based on subproject property

I'm trying to configure a Zip task based on one of the property inside sub-projects, but the property is not yet accessible at the time of configuring the task. For instance, I want to exclude all my projects that has toexclude = true from my zip file. So, the build.gradle of the sub-projects that I want to exclude starts with this:
ext.toexclude = true;
...
And my main build.gradle has this task:
task zipContent (type: Zip){
def excludedProjects = allprojects.findAll{Project p -> p.toexclude == true}.collect{it.name}
println excludedProjects
destinationDir = "/some/path"
baseName = "myFile.zip"
exclude excludedProjects
from "/some/other/path"
}
The problem is that excludedProjects is always empty. Indeed, when I am executing the task, I can see []. I believe this is due to the fact that the property that I set in the subproject's build.gradle is not available at the moment the task is configured. As a proof, if I replace the first line of the task by this:
def excludedProjects = allprojects.collect{it.name}
The task prints out all of my project's name, and the zip contains nothing (which means the problem is in the p.toexclude == true).
Also, if I try this:
task zipContent (type: Zip){
def excludedProjects = []
doFirst{
excludedProjects = allprojects.findAll{Project p -> p.toexclude == true}.collect{it.name}
println "IN DOFIRST"
println excludedProjects
}
println "IN TASK CONFIG"
println excludedProjects
destinationDir = "/some/path"
baseName = "myFile.zip"
exclude excludedProjects
from "/some/other/path"
}
The task prints out IN TASK CONFIG followed by an empty array, then IN DOFIRST with the array containing only the subprojects that I set ext.toexclude == true.
So, is there a way to get the properties of the sub-projects at configuration time?
Well, the crucial question is: At which point of the build is all necessary information available?
Since we want to know each project in the build, where the extra property toexclude is set to true and it is possible (and by design) that the property is set via the build script, we need each build script to be evaluated.
Now, we have two options:
By default, subprojects are evaluated after the parent (root) project. To ensure the evaluation of each project, we need to wait for the point of the build, where all projects are evaluated. Gradle provides a listener for that point:
gradle.addListener(new BuildAdapter() {
#Override
void projectsEvaluated(Gradle gradle) {
tasks.getByPath('zipContent').with {
exclude allprojects.findAll { it.toexclude }.collect{ it.name }
}
}
})
Gradle provides the method evaluationDependsOnChildren(), to turn the evaluation order around. It may be possible to use your original approach by calling this method before querying the excluded projects. Since this method only applies on child projects, you may try to call evaluationDependsOn(String) for each project in the build to also apply for 'sibling' projects. Since this solution breaks Gradle default behavior, it may have undesired side effects.
Just define excludedProjects outside the task
def excludedProjects = allprojects.findAll{Project p -> p.toexclude == true}.collect{it.name}
task zipContent (type: Zip){
destinationDir = file("/some/path")
baseName = "myFile.zip"
exclude excludedProjects
from "/some/other/path"
}
You can call evaluationDependsOnChildren() in the root project so that child projects are evaluated before the root
Eg
evaluationDependsOnChildren()
task zipContent (type: Zip) { ... }
Another option is to use an afterEvaluate { ... } closure to delay evaluation
Eg:
afterEvaluate {
task zipContent (type: Zip) { ... }
}

Gradle zip task neither failing nor working

I'm trying to create a custom Gradle 4.3.1 task that will:
Run ./gradlew build which produces a build/libs/myapp.jar artifact; then
Creates a myapp-1.0.zip ZIP file whose contents include:
build/libs/myapp.jar; and
./AppGuide.md; and
./app-config.json
Here's my best attempt:
task zipMeUp(type: Zip) {
String zipName = 'myapp-1.0.zip'
doFirst {
tasks.build
}
from 'build/libs/myapp.jar'
from 'AppGuide.md'
from 'app-config.json'
into zipName
}
When I run this (./gradlew zipMeUp) I get the following output:
HarveyZ:myapp myuser$ ./gradlew zipMeUp
BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed
But nothing actually seems to happen (no myapp-1.0.zip file in the directory). Any idea what the fix/solution is?
Don't use doFirst, use dependsOn
task zipMeUp(type:Zip, dependsOn :[build]) {
String zipName = 'myapp-1.0.zip'
from 'build/libs/myapp.jar'
from 'AppGuide.md'
from 'app-config.json'
version = "1.0"
baseName = "myapp"
}

Gradle clean erasing my file prior to ZIP task execution

I have the following simple task in my build:
task generateFile << {
def file = new File("$buildDir/setclasspath.sh")
file.text = "sample"
outputs.file(file)
}
task createDistro(type: Zip, dependsOn: ['copyDependencies','packageEnvironments','jar', 'generateFile']) <<{
from generateClasspathScript {
fileMode = 0755
into 'bin'
}
}
When I run gradle clean build I see the following output:
Cannot call TaskOutputs.file(Object) on task ':generateFile' after task has started execution. Check the configuration of task ':generateFile' as you may have misused '<<' at task declaration
How do I declare the task file creation outputs as an input to the zip task while also ensuring they happen in the execution phase?
If I leave off the << then the clean task wipes the generated file before the ZIP can use it. If I keep them, I get the above error.
It's the opposite as what is being suggested in the comments. You are trying to set the outputs in execution phase. The correct way to do what you are probably trying to do is for example:
task generateFile {
def file = new File("$buildDir/setclasspath.sh")
outputs.file(file)
doLast {
file.text = "sample"
}
}

How to execute a shell command for each file in a Gradle file collection?

I've recently been directed to implement all build/release engineering jobs in Gradle.
So I'm new to Gradle and some things which are drop-dead obvious at a shell prompt are just hard to fathom in this brave new world.
My build creates a set of jar files.
I need to execute a shell task for each one of them
Something similar to:
task findBugs (type:Exec, dependsOn: fb23) {commandLine 'java', '-jar',
'./findBugs/findbugs.jar', '-textui', '-progress', '-xml', '-output',
'TrustManagerService.xml', 'TrustManagerService.jar'}
(I can't usethe FindBugs plug-in because we're in JDK8 - so I'm running FindBugs externally from a preview version...)
I can get a file collection and iterate over the file names with something like:
FileTree iotTree = fileTree(dir: '.')
iotTree.include '*.jar'
iotTree.each {
File file -> println file.name
println 'A file'
}
or
def iotJars = files(file('./jars').listFiles())
println ''
println iotJars
iotJars.each { File file -> println 'The next file is: ' + file.name }
But for the life of me I can't see how to execute a shell command for each filename returned...
Thoughts on the most straightforward way to do this??
By the way - what's the best way to include the line breaks in the code listing - I had to return and add CRs to each line to get them to break...:(
You can use the execute method on a String to execute a shell command.
Here is an example task:
task executeExample << {
FileTree ioTree = fileTree(dir: ".")
ioTree.each { f ->
def proc = "ls -l ${f}".execute();
proc.waitFor();
println "return code: ${ proc.exitValue()}"
println "stderr: ${proc.err.text}"
println "stdout: ${proc.in.text}"
}
}

Resources