Possible to use a nested copy spec to process a subset of jar target resources?


(Blaine Simpson) #1

Is this possible? I have two use cases for this right now. I want to run a filter on files matching a certain pattern in src/main/resources, and I want to run a rename closure only on files in a specific list. I know that I could do this with a standalone copy, but then it wouldn’t end up in the project’s output jar.

Adding nested copy specs to the jar closure adds instead of replaces output elements. That’s understandable, so I just exclude the same elements from the root level-- the that doesn’t work because once something is excluded form the root level, it is absolutely excluded and attempts to pull it in from nested specs have no effect. To add some concreteness to this aspect:

jar {
    exclude '**/.*/**'
    into('extra') {
        from 'src/main/resources'
        include 'a*'
    }
}

That duplicates a* elements in the jar, and if I add “exclude a*” at at the root level, the into block has no effect since a* artifacts have been excluded.

There must be another way to achieve my goal, because I can not be the only Gradle user who wants to filter jar file artifacts but who doesn’t want to filter binary files.


(Matias Bjarland) #2

There might be a cleaner solution but I think the below might solve your problem. Apologies for a long post, but I use these questions as excuses to learn a bit more about gradle so here goes.

Sample project zip can be downloaded here.

Given the following build file:

apply plugin: 'java'
  jar {
  exclude '**/.*/**'
  from('other/main/resources') {
    eachFile { FileCopyDetails d ->
      if (d.relativePath.pathString.startsWith("one")) {
        d.relativePath = RelativePath.parse(true, "extra/${d.relativePath}")
      }
    }
  }
      includeEmptyDirs = false
}

and the following project directory structure:


nadurra:jarrename mbjarland$ find . -type f  ./build.gradle  ./other/main/resources/one/a.txt  ./other/main/resources/one/b.txt  ./other/main/resources/two/c.log  ./other/main/resources/two/d.log  

executing “gradle jar” gives the following jar file:

nadurra:jarrename mbjarland$ unzip -l build/libs/jarrename.jar
 Archive:
build/libs/jarrename.jar
  Length
   Date
 Time
  Name
 --------
  ----
 ----
  ----
        0
12-04-11 16:59
 META-INF/
       25
12-04-11 16:59
 META-INF/MANIFEST.MF
        0
12-04-11 16:59
 extra/
        0
12-04-11 16:59
 extra/one/
       12
12-04-11 16:29
 extra/one/a.txt
       12
12-04-11 16:35
 extra/one/b.txt
        0
12-04-11 16:35
 two/
        4
12-04-11 16:35
 two/c.log
        5
12-04-11 16:35
 two/d.log
 --------
                 -------
       58
                 9 files

For reference, I took a brief cruise (quite brief, so gradle gurus might slap me on this) through the gradle code and it looks like the jar task extends the zip task which in turn uses the CopyActionImpl class for the into/from/etc syntax. This in turn means that the jar task syntax essentially behaves like the copy task syntax. Relevant links:

Copy DSL CopySpec javadoc FileCopyDetails javadoc RelativePath javadoc

Also, looking at the unit tests for gradle has helped me out more than once. For the above, take a look at CopyTaskIntegrationTest.groovy at line 90 or line 202. Tests as executable documentation. Nice!


(Blaine Simpson) #3

Your sample code doesn’t touch my use case. If I pull in inputs from other directories, I can add new copy specs (like you did) and do any filtering that I want. I want to use the conventional directories, like ‘src/main/webapp’ for my web apps (which use the war pugin). Ditto for the unit test at line 90.

But thanks for taking the time, for the general tip about checking the unit tests, and for pointing me to the line 202 test…

Though that test does not filter a subset of files in the original set, I saw that the ‘fcd.exclude()’ is conditional and I tried the same thing with ‘fcd.filter{…}’ and it works.

jar {
    exclude '**/.*/**'
    int i = 10
    eachFile { fcd -> if (fcd.name.startsWith('a')) {
        fcd.filter { 'line ' + (++i) }
        // Following just to test that the input a* files are not output twice
        //fcd.relativePath = fcd.relativePath.prepend('pref')
    } }
}

Thanks Bud.


(Matias Bjarland) #4

LOL. Well glad you figured it out. My bad on misreading your initial issue.


(Luke Daley) #5

Nice job on finding the solution :slight_smile: