Copy files from multiple locations to one destination with Gradle copy task

The default behavior of Gradle copy task is copy the file and keep their directory structure.

Suppose, you have multiple project directories, each of them contains one or more markdown files, these files may nested deep in the directory structure. Now you want to collect them and copy them into one place, that means all the md files will be copied to one location.

For example, the sources

 
/dir1/readme.md
/dir2/readme.md
/dir2/test/readmetest.md
/dir3/readme.md
/dir3/doc/doc.md
 

Since some of them have the same name, we will need to rename them while copying to avoid file name conflict. We will append a number to the end of file name and increase the number for each copying. The destination folder should look like this:

 
/dest/readme1.md
/dest/readme2.md
/dest/readmetest3.md
/dest/readme4.md
/dest/doc5.md

If you write like this:

 
task copyMd( type:Copy ) {
  from 'dir1'
  from 'dir2'
  from 'dir3'
  include '**/*.md'
  into 'dest'
 
  def n = 0
  rename { String fileName ->
    n = n + 1
    fileName - '.md' + n + '.md'
  }
  includeEmptyDirs = false
}
 

The result will be

 
/dest/readme1.md
/dest/readme2.md
/dest/test/readmetest3.md
/dest/readme4.md
/dest/doc/doc5.md

The files matching by '**' always keep its directory structure when copying.

I can't find the built in way to do this. But we can write a helper function which will list the files and filter out files ends with md extension and pass them to from, so the from accept file list, not just a directory.

Here is the solution.

 
 
def listFilesRecursive( collection, path ) {
 
  col = files { file(path).listFiles() }
  collection += col
  for ( x in col ) {
    if ( x.isFile() ) continue
    else 
      collection = listFilesRecursive(collection, x.absolutePath)
  }
  return collection
 
}
 
task copyMd( type: Copy) {
  def n = 0
  def testcol = files('.')
  testcol = listFilesRecursive(testcol, 'F:/workspace/project')
 
  onlyMds = testcol.filter { File file ->
      file.name.endsWith('.md') && file.isFile()
  }
 
  from (onlyMds) {
    include '**/*.md'
    into '.'
  }
 
  into 'markdown'
  rename { String fileName ->
    n = n + 1
 
    fileName -    '.md' + (n) + '.md' 
  }
  includeEmptyDirs = false
}
 

Copy files around is quite a common task in any build process, this example illustrated that in Gradle you have both the powerful convention and the flexibility, all the features of Ant and Maven are at your disposal, but when you have a very specific requirement you can implement it by programming in Gradle by using Groovy language.

See Java and Groovy can not pass parameter by reference about why the listFilesRecursive is implemented like this.

See more about file operations in Gradle How to use file interfaces in Gradle project, the file, files, fileTree