Gradle CopySpec.eachFile 中的 FileCopyDetails 评估

Evaluation of FileCopyDetails in Gradle CopySpec.eachFile

我正在尝试做一件看似相当简单的事情:显示我正在复制的文件的目标路径 使用 Gradle Copy任务或方法。
为此,我使用 CopySpec.eachFile 方法,其中我使用 FileCopyDetails 访问目标路径,正如我认为在 Gradle javadoc.[=58= 中解释的那样]

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

task copyTaskUsingEachFile(type: Copy) {

  from("$projectDir/resources/file-with-tokens.txt") 
  into('generated/subfolder')

  filter(ReplaceTokens, tokens: [sampleProp: 'Hello sample property!'])
  eachFile {  println """ ---- content of FileCopyDetails object used in eachFile :
    object toString = $it 
    name = $it.name
    path = $it.path
    relativePath = $it.relativePath
    sourceName = $it.sourceName
    sourcePath = $it.sourcePath
    """ 
  }
}

问题是eachFile 中使用的基础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

我希望 Gradle 显示:

sourceName = resources/file-with-tokens.txt
path = generated/subfolder/file-with-tokens.txt

仔细阅读 FileCopyDetails 文档后:

Provides details about a file or directory about to be copied, and allows some aspects of the destination file to be modified

我开始认为,在 eachFile 中打印此对象时,尚未设置 "final" 目标路径。

我尝试使用 child CopySpec,使用 from (...) {...} 语法,猜测它可能会影响评估时间:

task copyTaskUsingEachFileAndChildCopySpec(type: Copy) {

  into 'generated'

  from("$projectDir/resources/file-with-tokens.txt") {
    into('subfolder')
  }

  filter(ReplaceTokens, tokens: [sampleProp: 'Hello sample property!'])
  eachFile {  println """ ---- content of FileCopyDetails object used in eachFile :
    object toString = $it 
    name = $it.name
    path = $it.path
    relativePath = $it.relativePath
    sourceName = $it.sourceName
    sourcePath = $it.sourcePath
    """ 
  }

}

这次我得到以下结果:

: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

所以所有目标路径都显示了它们的子 CopySpec 值,但不是从父 CopySpec 继承的部分(generated 在当前情况下)。

所以我有两个问题:

任何帮助或部分解释将不胜感激。


更新

使用 ,我知道在 CopySpec.eachFile 中成功打印了我复制的文件的完整 目标路径 。 为此,我使用了 Copy.destinationDir 字段。

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

现在唯一缺少的是能够以简单的方式打印复制文件的完整源路径
"easy way" 我的意思是,举例来说,在单个 eachFile 中,所有子 CopySpec 将继承(如上所述)。

目前,我可以使用Copy.source字段和FileCollection.getAsPath方法来显示它,但在eachFile.

中集成并不那么容易

更新 2

吸血鬼又说对了,你可以用FileCopyDetails.getFile()访问一个代表源的File对象,这样你就可以显示完整的源路径:

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')
  }
}

但是你不能在同时使用filter操作时使用FileCopyDetails.getFile()
这是一个神秘的 Gradle 错误,在此处解释:https://discuss.gradle.org/t/combining-eachfile-filter-results-in-failure/14801/2

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

public File getFile() {
    if (filterChain.hasFilters()) {
        throw new UnsupportedOperationException();
    } else {
        return fileDetails.getFile();
    }
}

不幸的是,这解释了这个问题。

无论如何,在任何其他情况下,我最初的 2 个问题都找到了新的答案,谢谢!


更新 3(最终)

再次感谢 Vampire(他专门针对此问题开设了一个 Gradle 案例),他为我们提供了前一个 filter / getFile 错误的解决方案。

查看他的 https://github.com/gradle/gradle/issues/1588 了解详细信息,但是,简而言之,为避免此问题,在 same CopySpec 中,您只需调用filter AFTER 调用 eachFile.
我确认它正在工作。

如果你更深入地阅读 JavaDoc,你会在 getPath()getRelativePath() 的第一句中找到解释,即 Returns the path of this file, relative to the root of the copy destination.

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

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

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

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


如果需要文件的源路径,直接使用FileCopyDetailsfile属性即可。每个 FileCopyDetails 也是一个 FileTreeElement(它的超类),因此有一个指向源文件的文件 属性(File 对象)。

请注意 filter 可能会受到干扰。如果您在同一复制规范中且在 eachFile 之前有一个 filter,或者在您执行 eachFile 的父复制规范中有一个 filter,无论如果之前或之后,那么你将得到一个 UnsupportedOperationException。如果您在同一复制规范中的 eachFile 之后执行 filter,它就可以工作。有关详细信息,请参阅 https://github.com/gradle/gradle/issues/1588