How to copy to multiple destinations with Gradle copy task? - gradle

I am trying to copy one file to multiple destinations through a Gradle task. I found the following in other websites but I get an ERROR while running this task.
def filesToCopy = copySpec{
from 'somefile.jar'
rename {String fileName -> 'anotherfile.jar'}
}
task copyFile(type:Copy) {
with filesToCopy {
into 'dest1/'
}
with filesToCopy {
into 'dest2/'
}
}
ERROR
No signature of method: org.gradle.api.internal.file.copy.CopySpecImpl.call() is applicable for argument types
Is there a way to copy to multiple destinations in one Gradle task?

an alternative way
task myCustomTask << {
copy {
from 'sourcePath/folderA'
into 'targetPath/folderA'
}
copy {
from 'sourcePath/folderB'
into 'targetPath/folderB'
}
copy {
from 'sourcePath/fileA.java','sourcePath/fileB.java'
into 'targetPath/folderC'
}
}

If you really want them in one task, you do something like this:
def filesToCopy = copySpec {
from 'someFile.jar'
rename { 'anotherfile.jar' }
}
task copyFiles << {
['dest1', 'dest2'].each { dest ->
copy {
with filesToCopy
into dest
}
}
}

Here is a general snippet without copySpec for Gradle 4.1. As pointed out the trick is to use a base into and use relative into inside the closures (e.g. from closure).
task multiIntoCopy(type: Copy){
into(projectDir) // copy into relative to this
from("foo"){
into("copied/foo") // will be projectDir/copied/foo
// just standard copy stuff
rename("a.txt", "x.txt")
}
from("bar"){
into("copied/aswell/bar") // projectDir/copied/copied/aswell/bar
}
from("baz") // baz folder content will get copied into projectDir
//from("/bar"){ // this will mess things up, be very careful with the paths
// into("copied/aswell/bar")
//}
}

With a Common Destination Base Path
If your destination paths share a common path prefix (dest_base), then you can use something like this:
def filesToCopy = copySpec {
from 'somefile.jar'
rename { String fileName -> 'anotherfile.jar' }
}
task copyFile(type: Copy) {
into 'dest_base'
into('dest1') {
with filesToCopy
}
into('dest2') {
with filesToCopy
}
}
Compared to other answers which use the copy method, this approach also retains Gradle’s UP-TO-DATE checks.
The above snippet would result in output like this:
dest_base/
├── dest1
│   └── anotherfile.jar
└── dest2
└── anotherfile.jar

no there isn't a way to do that atm. I would create seperate gradle tasks for each target directory
def filesToCopy = copySpec{
from 'somefile.jar'
rename {String fileName -> 'anotherfile.jar'}
}
task copyFileDest1(type:Copy) {
with filesToCopy
into 'dest1/'
}
task filesToCopyDest2(type:Copy) {
with filesToCopy
into 'dest2/'
}

I needed to do this in a particular order to rewrite a particular string on a set of files.
task copyAtoB(dependsOn: [existingTask]) {
doLast {
copy {
from("folder/a") {
include "*.java"
}
// Have to use a new path for modified files
into("folder/b")
filter {
String line ->
line.replaceAll("changeme", "to this")
}
}
}
}
task overwriteFilesInAfromB(dependsOn: [copyAtoB]) {
doLast {
copy {
from("folder/b") {
include "*.java"
}
into("folder/a")
}
}
}
// Finally, delete the files in folder B
task deleteB(type: Delete, dependsOn: overwriteFilesInAfromB) {
delete("folder/b")
}
nextTask.dependsOn(deleteB)

Related

Gradle zip: how to include and rename one file easily?

Create a zip adding file "hello/world.xml" under directory "foo/bar" as "hello/universe.xml"
task myZip(type: Zip) {
from ("foo/bar") {
include "hello/world.xml"
filesMatching("hello/*") {
it.path = "hello/universe.xml"
}
}
}
filesMatching(...) will impact performance obviously.
What is a better way? like:
task myZip(type: Zip) {
from ("foo/bar") {
include ("hello/world.xml") {
rename "hello/universe.xml"
}
}
}
But rename is not supported with include.
I don't get why you are using filesMatching at all. You are only including one single file in your child CopySpec. Simply rename it and everything is fine:
task myZip(type: Zip) {
from ('foo/bar') {
include 'hello/world.xml'
rename { 'hello/universe.xml' }
}
}
If you want to include multiple files (or even copy all), but only want to rename one of them, specify which file(s) to rename with a regular expression as first argument:
task myZip(type: Zip) {
from 'foo/bar'
rename 'hello/world.xml' 'hello/universe.xml'
}
If the last one did not work, try:
rename ('a.java', 'b.java')

What is the best way to parameterize a task on Gradle?

I need to create two different .properties files from two different .properties.dist if they don't exist, so I'm using a Copy task and I'm specifying the from and the into accordingly.
At the moment I had to create two different tasks, each of which is creating a file like this:
task copyAndRenameDialling(type: Copy){
if(!file("./properties/dialling.properties").exists()){
from './dist/dialling.properties.dist'
into './properties/'
rename{ String fileName ->
fileName.replace('.dist','')
}
}
}
task copyAndRenameFiles(type: Copy){
if(!file("./properties/file.properties").exists()){
from './dist/files.properties.dist'
into './properties/'
rename{ String fileName ->
fileName.replace('.dist','')
}
}
}
task copyAndRenameProperties {
dependsOn << copyAndRenameDialling
dependsOn << copyAndRenameFiles
}
and I run the task with gradle copyAndRenameProperties.
Is it possible to make the two Copy tasks parameterized based on the name of the file, so that I have only one generic copyAndRename?
If so, how can I pass the parameter to the task?
I'd do it like this:
task copyAndRenameProperties(type: Copy) {
from 'dist'
include '*.properties.dist'
into 'properties'
rename { it - ~/\.dist$/ }
eachFile { if (file("properties/$it.name").file) it.exclude() }
}
or if you really only want those two specific files
task copyAndRenameProperties(type: Copy) {
from 'dist/dialling.properties.dist'
from 'dist/file.properties.dist'
into 'properties'
rename { it - ~/\.dist$/ }
eachFile { if (file("properties/$it.name").file) it.exclude() }
}
or
task copyAndRenameProperties(type: Copy) {
from 'dist'
include 'dialling.properties.dist', 'file.properties.dist'
into 'properties'
rename { it - ~/\.dist$/ }
eachFile { if (file("properties/$it.name").file) it.exclude() }
}

Gradle Zip Task: Replace Entire File Content While Being Copied

I currently have a set of files that contain a DSL that needs to be parsed and converted to XML before being copied over to the destination build directory.
I'm using the eachFile hook to accomplish this, but when I replace the content of the file the source file is being changed as well:
task build(type: Zip) {
with {
archiveName = "${project.name}-${project.version}.${extension}"
destinationDir = buildDir
}
from('workflow/dsl') {
eachFile { fileDetails ->
String xml = new OozieDslParser().parse(fileDetails.getFile())
fileDetails.setName(fileDetails.getName().replaceFirst(~/\.[^\.]+$/, '.xml')
fileDetails.getFile().text = xml //This changes the source file as well.
}
}
from('workflow/resources')
}
What is the best way to approach this problem?
Unfortunately, the 'expand' and 'filter' options don't seem to work as the former just expands properties and the latter only feeds me one line at a time.
Thanks!
I used a custom FilterReader to solve this problem:
class OozieDslFilter extends FilterReader {
OozieDslFilter(Reader input) {
super(new StringReader(new OozieDslParser().parse(input.text)))
}
}
task build(type: Zip) {
with {
archiveName = "${project.name}-${project.version}.${extension}"
destinationDir = buildDir
}
from('workflow/resources')
from('workflow/dsl') {
rename { it - ~/\.[^\.]+$/ + '.xml' }
filter(OozieDslFilter)
}
}

How can I define two different 'distribution' tasks in gradle?

The normal behavior of the distTar and distZip tasks from the application plugin in gradle seems to be to copy the contents of src/dist into the zip and tar files, but I have a subfolder in src/dist that I want to exclude from the default distribution, and include it for a new (extended) task, possibly to be called distZipWithJRE.
I have been able to exclude this folder in the default task as follows:
distributions.main {
contents {
from('build/config/main') {
into('config')
}
from('../src/dist') {
exclude('jre')
}
}
}
How can I define a second task that behaves just like the original (unmodified) task?
Using Gradle 4.8 I had to tweak the answer to use 'with' from CopySpec instead
distributions {
zipWithJRE {
baseName = 'zipWithJRE'
contents {
with distributions.main.contents
}
}
}
It seems that what you're looking for is in the docs. You need to leave current settings as is and for zipWithJRE create and configure custom distribution:
distributions {
zipWithJRE {
baseName = 'zipWithJRE'
contents {
from { distributions.main.contents }
}
}
}

Gradle copy without overwrite

Is there a way to avoid overwriting files, when using task type:Copy?
This is my task:
task unpack1(type:Copy)
{
duplicatesStrategy= DuplicatesStrategy.EXCLUDE
delete(rootDir.getPath()+"/tmp")
from zipTree(rootDir.getPath()+"/app-war/app.war")
into rootDir.getPath()+"/tmp"
duplicatesStrategy= DuplicatesStrategy.EXCLUDE
from rootDir.getPath()+"/tmp"
into "WebContent"
}
I want to avoid to specify all the files using exclude 'file/file*'.
It looks like that duplicatesStrategy= DuplicatesStrategy.EXCLUDE doesn't work. I read about an issue on gradle 0.9 but I'm using Gradle 2.1.
Is this problem still there?
Or am I misunderstanding how this task should be used properly?
Thanks
A further refinement of BugOrFeature's answer. It's using simple strings for the from and into parameters, uses the CopySpec's destinationDir property to resolve the destination file's relative path to a File:
task ensureLocalTestProperties(type: Copy) {
from zipTree('/app-war/app.war')
into 'WebContent'
eachFile {
if (it.relativePath.getFile(destinationDir).exists()) {
it.exclude()
}
}
}
You can always check first if the file exists in the destination directory:
task copyFileIfNotExists << {
if (!file('destination/directory/myFile').exists())
copy {
from("source/directory")
into("destination/directory")
include("myFile")
}
}
Sample based on Peter's comment:
task unpack1(type: Copy) {
def destination = project.file("WebContent")
from rootDir.getPath() + "/tmp"
into destination
eachFile {
if (it.getRelativePath().getFile(destination).exists()) {
it.exclude()
}
}
}

Resources