title: Evaluation of FileCopyDetails in Gradle CopySpec.eachFile
body: <p>I am trying to do what seems to be a rather simple thing: <strong>displaying the destination path of the files that I'm copying</strong> using Gradle <code>Copy</code>task or method.<br>
  To do so, I am using the <code>CopySpec.eachFile</code> method, in which I use the use the <code>FileCopyDetails</code> to access the destination path, as I thought was explained in the <a href="https://docs.gradle.org/current/javadoc/org/gradle/api/file/FileCopyDetails.html" rel="nofollow noreferrer">Gradle javadoc</a>.</p>
  
  <p>Here is an example using a <code>Copy</code> task (same behaviour with <code>Copy</code> method):</p>
  
  <pre><code>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
      """ 
    }
  }
  </code></pre>
  
  <p><strong>Problem is</strong>: every attribute of the underlying <code>FileCopyDetails</code> object used in <code>eachFile</code> <strong>evaluates to the same value</strong>:</p>
  
  <pre><code>:copyTaskUsingEachFile
   ---- content of FileCopyDetails object used in eachFile :
      object toString = file 'C:\&lt;some path&gt;\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
  </code></pre>
  
  <p>I would have wanted Gradle to display:</p>
  
  <blockquote>
    <p>sourceName = <strong>resources</strong>/file-with-tokens.txt<br>
    path = <strong>generated/subfolder</strong>/file-with-tokens.txt</p>
  </blockquote>
  
  <p>After reading more carefully the <code>FileCopyDetails</code> doc:</p>
  
  <blockquote>
    <p>Provides details about a file or directory <strong>about to be copied</strong>, and allows some aspects of the destination file to be modified</p>
  </blockquote>
  
  <p>I start thinking that, at the time this object is printed in <code>eachFile</code>, the "final" destination path is not yet set.  </p>
  
  <p>I tried to use a <strong>child CopySpec</strong>, with the <code>from (...) {...}</code> syntax, guessing it could impact the evaluation time:</p>
  
  <pre><code>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
      """ 
    }
  
  }
  </code></pre>
  
  <p>And this time I get the following result:</p>
  
  <pre><code>:copyTaskUsingEachFileAndChildCopySpec
   ---- content of FileCopyDetails object used in eachFile :
      object toString = file 'C:\&lt;some path&gt;\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
  </code></pre>
  
  <p>So all destination paths are displayed with their child CopySpec value, but not the part inherited from the parent CopySpec (<code>generated</code> in the current case).</p>
  
  <p>So I have 2 questions:</p>
  
  <ul>
  <li>Does someone know how / when the <code>FileCopyDetails</code> is resolved in a <code>CopySpec.eachFile</code>?</li>
  <li>Does someone know how to display the complete destination path of the files copied by a <code>Copy</code> task (or method)?</li>
  </ul>
  
  <p>Any help or part of explanation will be greatly appreciated.</p>
  
  <hr>
  
  <p><strong>UPDATE</strong></p>
  
  <p>Using <a href="https://stackoverflow.com/a/42670945/1809195">Vampire answer</a>, I know succeed in printing, in <code>CopySpec.eachFile</code>, the complete <strong>destination path</strong> of my copied files.
  To do, I used <a href="https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:destinationDir" rel="nofollow noreferrer"><code>Copy.destinationDir</code></a> field.</p>
  
  <pre><code>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')
    }
  }
  </code></pre>
  
  <p>Which gives:</p>
  
  <pre><code> ------------ TASK ------------ copyMultipleFilesDisplayTargetFolder
  
  :copyMultipleFilesDisplayTargetFolder
  file file-with-tokens.txt copied to C:\&lt;some path&gt;\generated/subfolder/file-with-tokens.txt
  
  file file-with-tokens-2.txt copied to C:\&lt;some path&gt;\generated/subfolder2/file-with-tokens-2.txt
  
  file file-with-tokens-3.txt copied to C:\&lt;some path&gt;\generated/subfolder3/file-with-tokens-3.txt
  </code></pre>
  
  <p>The only missing thing is now to be able to print the complete <strong>source path</strong> of the copied files, in an <strong>easy way</strong>.<br>
  By <em>"easy way"</em> I mean, by example, in a single <code>eachFile</code> that would be inherited by all the child <code>CopySpec</code> (as above).</p>
  
  <p>For now, I can display it using <a href="https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:source" rel="nofollow noreferrer"><code>Copy.source</code></a> field and <code>FileCollection.getAsPath</code> method, but it isn't that easy to integrate in <code>eachFile</code>.</p>
  
  <hr>
  
  <p><strong>UPDATE 2</strong></p>
  
  <p>Vampire is right again, you can use the <code>FileCopyDetails.getFile()</code> to access a <code>File</code> object representing the source, which allow you to display the <strong>complete source path</strong>:</p>
  
  <pre><code>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')
    }
  }
  </code></pre>
  
  <p><strong>BUT</strong>, <strong>you can't use <code>FileCopyDetails.getFile()</code> when also using a <code>filter</code> operation!</strong><br>
  This is an arcane Gradle bug explain here: <a href="https://discuss.gradle.org/t/combining-eachfile-filter-results-in-failure/14801/2" rel="nofollow noreferrer">https://discuss.gradle.org/t/combining-eachfile-filter-results-in-failure/14801/2</a></p>
  
  <p>When looking at Gradle source, the <code>DefaultFileCopyDetails</code> class contains the following code:</p>
  
  <pre class="lang-java prettyprint-override"><code>public File getFile() {
      if (filterChain.hasFilters()) {
          throw new UnsupportedOperationException();
      } else {
          return fileDetails.getFile();
      }
  }
  </code></pre>
  
  <p>Which, unfortunately, explains the issue.</p>
  
  <p>Anyway, in any other case, my 2 initial questions have new found an answer, thanks !</p>
  
  <hr>
  
  <p><strong>UPDATE 3 (final)</strong></p>
  
  <p>And again thanks for Vampire (who opened a Gradle case especially for this issue), who gave us a solution for the former <code>filter</code> / <code>getFile</code> bug.</p>
  
  <p>See his <a href="https://github.com/gradle/gradle/issues/1588" rel="nofollow noreferrer">https://github.com/gradle/gradle/issues/1588</a> for the details, but, in a few words, to avoid the problem, in <strong>same CopySpec</strong>, you just have to place the call to <code>filter</code> <strong>AFTER</strong> the call to <code>eachFile</code>.<br>
  I do confirm it's working.</p>