评估Gradle CopySpec.eachFile中的FileCopyDetails

评估Gradle CopySpec.eachFile中的FileCopyDetails,gradle,path,copy,evaluation,Gradle,Path,Copy,Evaluation,我正在尝试做一件似乎相当简单的事情:使用GradleCopy任务或方法显示我正在复制的文件的目标路径。 为此,我使用了CopySpec.eachFile方法,在该方法中,我使用了使用FileCopyDetails来访问目标路径,正如我在中所解释的那样 下面是一个使用Copy任务的示例(与Copy方法的行为相同): 问题在于:在每个文件中使用的基础FileCopyDetails对象的每个属性的计算结果都是相同的值: :copyTaskUsingEachFile ---- content of F

我正在尝试做一件似乎相当简单的事情:使用Gradle
Copy
任务或方法显示我正在复制的文件的目标路径。
为此,我使用了
CopySpec.eachFile
方法,在该方法中,我使用了使用
FileCopyDetails
来访问目标路径,正如我在中所解释的那样

下面是一个使用
Copy
任务的示例(与
Copy
方法的行为相同):

问题在于:在
每个文件中使用的基础
FileCopyDetails
对象的每个属性的计算结果都是相同的值

:copyTaskUsingEachFile
 ---- content of FileCopyDetails object used in eachFile :
    object toString = file 'C:\<some path>\resources\file-with-tokens.txt'
    name = file-with-tokens.txt
    path = file-with-tokens.txt
    relativePath = file-with-tokens.txt
    sourceName = file-with-tokens.txt
    sourcePath = file-with-tokens.txt
task copyMultipleFilesDisplayTargetFolder(type: Copy) {

  printTask 'copyMultipleFilesDisplayTargetFolder'

  into 'generated'
  //filter(ReplaceTokens, tokens: [sampleProp: 'Hello sample property!'])

  eachFile { 
    println "file $it.name copied to $destinationDir/$it.path" 

    /* !!!! BEWARE !!!!
    You can't use the below FileCopyDetails.getFile() when also using a filter operation.
    Check https://discuss.gradle.org/t/combining-eachfile-filter-results-in-failure/14801/2
    */
    println "Initial source path: ${it.getFile().getCanonicalPath()}"

    println ''
  }

  from("$projectDir/resources/file-with-tokens.txt") {
    into('subfolder')
  }
  from("$projectDir/resources2/file-with-tokens-2.txt") {
    into('subfolder2')
  }
  from("$projectDir/resources3/file-with-tokens-3.txt") {
    into('subfolder3')
  }
}
这次我得到了以下结果:

:copyTaskUsingEachFileAndChildCopySpec
 ---- content of FileCopyDetails object used in eachFile :
    object toString = file 'C:\<some path>\resources\file-with-tokens.txt'
    name = file-with-tokens.txt
    path = subfolder/file-with-tokens.txt
    relativePath = subfolder/file-with-tokens.txt
    sourceName = file-with-tokens.txt
    sourcePath = file-with-tokens.txt
其中:

 ------------ TASK ------------ copyMultipleFilesDisplayTargetFolder

:copyMultipleFilesDisplayTargetFolder
file file-with-tokens.txt copied to C:\<some path>\generated/subfolder/file-with-tokens.txt

file file-with-tokens-2.txt copied to C:\<some path>\generated/subfolder2/file-with-tokens-2.txt

file file-with-tokens-3.txt copied to C:\<some path>\generated/subfolder3/file-with-tokens-3.txt
但是在同时使用
过滤器
操作时,不能使用
FileCopyDetails.getFile()

这是一个神秘的Gradle bug,请在此处解释:

查看Gradle源代码时,
DefaultFileCopyDetails
类包含以下代码:

公共文件getFile(){
if(filterChain.hasFilters()){
抛出新的UnsupportedOperationException();
}否则{
返回fileDetails.getFile();
}
}
不幸的是,这解释了这个问题

无论如何,在任何其他情况下,我最初的两个问题有了新的答案,谢谢


更新3(最终版)

再次感谢吸血鬼(他专门为这个问题打开了一个Gradle案例),他为我们提供了前一个
过滤器
/
getFile
bug的解决方案

有关详细信息,请参见his,但简单地说,为了避免出现问题,在同一CopySpec中,您只需在调用
每个文件后调用
过滤器


我确认它正在工作。

如果你更彻底地阅读JavaDoc,你会在
getPath()
getRelativePath()
的第一句中找到解释,它们说
返回此文件的路径,相对于复制目的地的根。

在第一个示例中,调用
eachFile
方法的
CopySpec
的复制目标是
generated/subfolder
,因此目标文件的相对路径是
file with tokens.txt

在第二个示例中,调用
eachFile
方法的
CopySpec
的复制目标是
生成的
,因此目标文件的相对路径是
子文件夹/file with tokens.txt

CopySpec
的复制目标与相对路径连接起来,您将获得目标文件的完整路径

如果你是e。G在调用
eachFile
之前,添加一个
rename
调用,比如
rename{'foo-'+it}
,然后您会看到相对路径将变成
foo file with tokens.txt
,而源路径仍然是
file with tokens.txt


如果需要文件的源路径,只需使用
FileCopyDetails
file
属性即可。每个
FileCopyDetails
也是一个
filetreeeElement
(其超类),因此具有指向源文件的文件属性(
file
对象)


请注意,
filter
s可能存在干扰。如果在同一复制规范中以及在
每个文件
之前有一个
过滤器
,或者在执行
每个文件
的父副本规范中有一个
过滤器
,无论是在之前还是之后,都会得到一个
不支持操作异常
。如果在同一复制规范中的
每个文件
之后执行
过滤器
,则它会工作。请参阅以了解更多信息。

我更新了我的答案。;-)顺便说一句,您不需要从(“$projectDir/resources2/file-with-tokens-2.txt”)执行
,只需从('resources2/file-with-tokens-2.txt')执行
。相对路径的处理方式与给定给
项目文件(对象…
的路径类似,因此与项目目录相对。关于
过滤器问题,请参阅我的最新答案更新。:-)
task copyMultipleFilesDisplayTargetFolder(type: Copy) {

  printTask 'copyMultipleFilesDisplayTargetFolder'

  into 'generated'
  filter(ReplaceTokens, tokens: [sampleProp: 'Hello sample property!'])

  /* Here, we print the complete destination path of the copied files, BUT,
     I don't know how to print their complete SOURCE path.
  */
  eachFile { 
    println "file $it.name copied to $destinationDir/$it.path" 
    println ''
  }

  from("$projectDir/resources/file-with-tokens.txt") {
    into('subfolder')
  }
  from("$projectDir/resources2/file-with-tokens-2.txt") {
    into('subfolder2')
  }
  from("$projectDir/resources3/file-with-tokens-3.txt") {
    into('subfolder3')
  }
}
 ------------ TASK ------------ copyMultipleFilesDisplayTargetFolder

:copyMultipleFilesDisplayTargetFolder
file file-with-tokens.txt copied to C:\<some path>\generated/subfolder/file-with-tokens.txt

file file-with-tokens-2.txt copied to C:\<some path>\generated/subfolder2/file-with-tokens-2.txt

file file-with-tokens-3.txt copied to C:\<some path>\generated/subfolder3/file-with-tokens-3.txt
task copyMultipleFilesDisplayTargetFolder(type: Copy) {

  printTask 'copyMultipleFilesDisplayTargetFolder'

  into 'generated'
  //filter(ReplaceTokens, tokens: [sampleProp: 'Hello sample property!'])

  eachFile { 
    println "file $it.name copied to $destinationDir/$it.path" 

    /* !!!! BEWARE !!!!
    You can't use the below FileCopyDetails.getFile() when also using a filter operation.
    Check https://discuss.gradle.org/t/combining-eachfile-filter-results-in-failure/14801/2
    */
    println "Initial source path: ${it.getFile().getCanonicalPath()}"

    println ''
  }

  from("$projectDir/resources/file-with-tokens.txt") {
    into('subfolder')
  }
  from("$projectDir/resources2/file-with-tokens-2.txt") {
    into('subfolder2')
  }
  from("$projectDir/resources3/file-with-tokens-3.txt") {
    into('subfolder3')
  }
}