I need to debug a Gradle Copy task.
How do I print for each copied file both "from" and "to"?
The task type Copy provides a method eachFile for this use case. The objects passed to the Action (or Closure) argument are of type FileCopyDetails. Those objects provide properties like sourcePath and path that can be used to log both the source and the destination of the copied file:
task myCopyTask(type: Copy) {
from ...
into ...
eachFile { file ->
println file.sourcePath
println file.path
}
}
Related
I need to create a gradle task to create a zip where I need to include all the json files that contain text "type":"customer" how can i include the check for containing text ?
is there a way I can do it:
task createZip(type: Zip) {
archiveName = "customer.zip"
destinationDir = file(testDir)
from(files(customer))
}
You could use a FileTree together with Gradle/Groovy filtering capabilities:
Let's say you have your source Customer JSON files under src/customers: you can define a Task like that:
task createZip(type: Zip) {
archiveName = "customer.zip"
destinationDir = buildDir
from fileTree('src/customers').filter { f -> f.text.contains('"type": "customer"')}
}
Note that this uses Groovy's File.getText() method to read the whole file content into memory to match the "type": "customer" expression. Be careful with performances if you have plenty or huge files to filter.
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
I have parent and child project. Parent's build.gradle is empty, settings.xml contains include 'child' and
in build.gradle of child I have a task
task('executionPath') << {
println projectDir
}
This task is supposed to print the path to project on which the build was started.
If I invoke it in root by ./gradlew executionPath I expect it to show path of the root project, e.g. C:\projects\parent.
If I invoke it in root by ./gradlew child:executionPath I expect it to show path of the child project, e.g. C:\projects\parent\child.
I've tried the following:
projectDir always path to child
new File('.') always path to parent
System.getProperty("user.dir") always path to parent
Answer Gradle: get folder from which "gradle" was executed is not helpful in my case. How can I achieve the above?
It's not very straightforward solution, but you can use start parameters to find out, whether the task was called for the root project or for the current. Something like this:
task('executionPath') << {
//find the argument representing current task
String calledTaskName = null;
for (String taskArgument : project.getGradle().startParameter.taskRequests.get(0).args) {
if (taskArgument.equals(name) || taskArgument.endsWith(':'+name)) {
calledTaskName = taskArgument;
}
}
if (calledTaskName == null) {
println 'Task was not called via arguments'
return;
}
//check, whether task was called on root project or for subproject only
if (calledTaskName.startsWith(project.getPath())) {
println projectDir
} else {
println System.getProperty("user.dir")
}
}
This task is looking within start parameters for the current task name. If it was called via start parameters, it checks, whether task name contains current project name as a prefix and according to it prints out current project path or root project path.
Unfortunately, I don't know any other solution for your case. Sure, you may need to modify it for your exact purposes.
I want to replace a value in our log4j2.xml with Gradle during build. I found a way to do that:
task reaplaceInLogFile {
String apiSuffix = System.properties['apiSuffix'] ?: ''
println "In my task"
String contents = file('src/main/resources/log4j2.xml').getText( 'UTF-8' )
println "File found"
contents = contents.replaceAll( "svc0022_operations", "svc0022_operations${apiSuffix}")
new File( 'src/main/resources/log4j2.xml' ).write( contents, 'UTF-8' )
}
However, this changes also the source file permanently and I do not want to do that. I want to change the log4j2.xml that will be included in the build zip only. I know I can use something like this:
tasks.withType(com.mulesoft.build.MuleZip) { task ->
String apiSuffix = System.properties['apiSuffix'] ?: ''
task.eachFile {
println name
if (name == 'mule-app.properties') {
println "Expanding properties for API Version suffix: ${apiSuffix}"
filter { String line ->
line.startsWith("${serviceId}.api.suffix") ? "${serviceId}.api.suffix=${apiSuffix}" : line
}
}
But I do not know what is the type of the log4j2 file. If there is another way to do that I will be thankful!
We are using Mule gradle plugin.
The Type is not the type of the log4j2 file, but the type of the task that creates the ZIP or wherever your log4j2 file is packaged into. If the log4j2 file is included in the ZIP that is generated by a MuleZip task, then you can simply add another if-branch for the log4j2 file.
But actually it is probably better to just edit the concrete task that packages up the log4j2 file into some archive instead of all tasks of the same type.
Besides that you should be able to use filesMatching instead of eachFile with an if I think.
Can gradle alter the structure of the tree while copying?
original
mod/a/src
mod/b/src
desired
dest/mod-a/source
dest/mod-b/source
dest/mod-c/source
I'm not sure where I should create a closure and override the copy tree logic
I'd like to do the gradle equivalent of ant's globmapper functionality
<property name="from.dir" location=".."/>
<property name="to.dir" location="dbutil"/>
<copy>
<fileset dir="${from.dir}" ... />
<globmapper from="${from.dir}/*/db" to="${to.dir}"/>
</copy>
Thanks
Peter
When changing file name, rename seems a good approach. When changing path you can override eachFile and modify the destination path.
This works pretty well.
copy {
from("${sourceDir}") {
include 'modules/**/**'
}
into(destDir)
eachFile {details ->
// Top Level Modules
def targetPath = rawPathToModulesPath(details.path)
details.path = targetPath
}
}
....
def rawPathToModulesPath(def path) {
// Standard case modules/name/src -> module-name/src
def modified=path.replaceAll('modules/([^/]+)/.*src/(java/)?(.*)', {"module-${it[1]}/src/${it[3]}"})
return modified
}
Please see sample below. Gradle 4.3 does not have rename/move methods, so we can do renaming on the fly.
What was happened:
Load file tree into the memory. I used zip file from dependencies in my example
Filter items, which are in the target folder
All result items will have the same prefix: if we filter files from directory "A/B/C/", then all files will be like "A/B/C/file.txt" or "A/B/C/D/file.txt". E.g. all of them will start with the same words
In the last statement eachFile we will change final name by cutting the directory prefix (e.g. we will cut "A/B/C").
Important: use type of task "Copy", which has optimizations for incremental compilation. Gradle will not do file copy if all of items below are true:
Input is the same (for my case - all dependencies of scope "nativeDependenciesScope") with previous build
Your function returned the same items with the previous build
Destination folder has the same file hashes, with the previous build
task copyNativeDependencies(type: Copy) {
includeEmptyDirs = false
def subfolderToUse = "win32Subfolder"
def nativePack = configurations.nativeDependenciesScope.singleFile // result - single dependency file
def nativeFiles = zipTree(nativePack).matching { include subfolderToUse + "/*" } // result - filtered file tree
from nativeFiles
into 'build/native_libs'
eachFile {
print(it.path)
// we filtered this folder above, e.g. all files will start from the same folder name
it.path = it.path.replaceFirst("$subfolderToUse/", "")
}
}
// and don't forget to link this task for something mandatory
test.dependsOn(copyNativeDependencies)
run.dependsOn(copyNativeDependencies)
The following works, but is there a more gradle-ish way to do this?
ant.copy(todir: destDir) {
fileset( dir: "${srcDir}/module", includes: '**/src/**')
regexpmapper(from: '^(.*)/src/(.*)$', to: /module-\1\/src\/\2/)
}