CopySpec: support for moving files/directories?

Hi,

I’m stuck with a seemingly simple problem and I’m wondering if I’m missing something. I have a gradle task that assembles/generates some files/directories within a target directory. A second task should create an archive of just the content of one of the subdirectories created by the first task. However, the actual subdirectory shouldn’t show up in the archive:

The first task looks something like this:

task assembleStuff(type: Copy) {
    ...
    into targetDir
}

… and produces something like that:

  targetDir

|-- file0

|-- subdir1

| |- file1A

| \- file1B

\-- subdir2

  |- file2A

  \- file2B  

I want the Zip to contain just:

  archive.zip

|-- file1A

\-- file1B  

I tried using rename:

  task zipSomeStuff(type: Zip) {



from assembleStuff



include 'subdir1/*'



rename 'subdir1/(.*)', '$1'  }  

but the rename methods seem to only work at filename level. There doesn’t seem to be a way to move files across directories while copying, in fact, not even a way to rename directories while copying…

I found this old post: http://gradle.1045684.n5.nabble.com/CopySpec-remapTarget-is-gone-td1436423.html and there doesn’t seem to be an alternative to remapTarget even now… or am I missing something?

Regards, Mike

For something like this, you need to use the [‘eachFile {}’](http://gradle.org/docs/current/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:eachFile(groovy.lang.Closure) hook.

Thanks for putting me into the right direction, Luke.

There is, however, a little problem remaining: while I’m now able to move the files into the right (root) directory, the copySpec still creates the (now empty) “subdir1” folder.

What I have now is:

  task zipSomeStuff(type: Zip) {



from assembleStuff



eachFile { fileCopyDetails ->





 fileCopyDetails.path = fileCopyDetails.path.replaceAll(/(.*\/)subdir1\/(.*)/, '$1$2')



}



//exclude '**/subdir1/' // Results in no files being copied at all



includeEmptyDirs = false // Doesn't seem to have any effect in my case  }  

While you are developing this, make sure you are cleaning the output dir each time. There are some bugs in the copy task that throw out the incremental build.

Yep, I noticed that, I’m emptying the build folder manually each time I change something in the buildfile, but it still looks like that directory remains…

Oh, I also tried:

  ...  eachFile { fileCopyDetails ->



if (fileCopyDetails.file.isDirectory() && fileCopyDetails.file.name == "subdir1") {





 fileCopyDetails.exclude()





 println "DIR: ${fileCopyDetails.path}"



}  }  

But the hook seems to only be called for files.

I just tried this locally and it worked for me. Any chance you could send me a sample that I can work with? (luke.daley at gradleware.com)

Okay, it took me a while but I finally found the difference between my simplified version and my real-life code. Here is a working example that should clarify what I have:

  task assembleStuff() {

ext.destinationDir = "${buildDir}/stuff/"

outputs.dir destinationDir

  doLast {

 mkdir(destinationDir)

 file("${destinationDir}/file0").text = "stuff0"

 [1, 2].each { num ->

  mkdir("${destinationDir}/subdir${num}/")

  ['A','B'].each { let ->



file("${destinationDir}/subdir${num}/file${num}${let}").text = "stuff${num}${let}"

  }

 }

}  }

 task createStuffInIrrelevantDir() {

ext.destinationDir = "${buildDir}/irrelevant/"

outputs.dir destinationDir

  doLast {

 mkdir("${destinationDir}/")

 mkdir("${destinationDir}/iwantThisEmptyDir/")

 file("${destinationDir}/irrelevantfile").text = "foo"

}  }

 task zipSomeStuff(type: Zip) {

destinationDir = file("${buildDir}")

baseName = 'someStuff'

from { assembleStuff } {

 eachFile { fileCopyDetails ->

  fileCopyDetails.path = fileCopyDetails.path.replaceAll(/(.*\/?)subdir1\/(.*)/, '$1$2')

 }

 include '**/subdir1/**'

 into 'myPath/'

 includeEmptyDirs = false

}

  from { createStuffInIrrelevantDir } {

 into 'irrelevantPath/'

}  }  

The problem is the “includeEmptyDirs = false”. I found that it only works when put directly in the root copySpec, but not when it’s used in a nested copySpec (like above). When put in a nested CopySpec, the includeEmptyDirs switch isn’t honored and (and this is why it took me so long to find it out) no error / warning about deprecated dynamic properties is issued.

Is this a bug?

My problem (yes I have a pretty complex copy task ;)) is that I don’t want the empty dir in the first nested copySpec, but I do want the emptyDir from the second one (from the createStuffInIrrelevantDir task).

Yeah, it’s a bug. GRADLE-1830 essentially covers this.

Okay, thanks for the feedback! :slight_smile:

Is there a suggested workaround for this? I guess deleting the directories in a doLast block would be an option when using the Copy task, but what about the Zip task?

See the workaround mentioned on the ticket.