Right way to copy contents from dependency archives?

Hi,

I have a configuration that contains some dependencies. I want a task that extracts all resolved artifacts into a directory, but I’m getting stuck with Gradle’s Lifecycle I think…

Here’s what I want:

configurations {
 api
}
  dependencies {
 api ( group: 'somegroup', name: 'someArtifact', version: 'someVersion' )
}
  task extractApi(type: Copy) {
 configurations.api.asFileTree.each {
  from( zipTree(it) )
 }
 into file("${project.buildDir}/api/")
}

But I’m getting: > * What went wrong: > A problem occurred evaluating project ‘:myproject’. > > You can’t change a configuration which is not in unresolved state!

… which makes sense: I’m trying to configure a copy task (gradle configuration phase) with configuration dependencies, which would have to be resolved first, but they may not even be readily configured, so they can’t…

The following doesn’t work as well:

task extractApi(type: Copy) {
 into file("${project.buildDir}/api/")
 doFirst {
  configurations.api.asFileTree.each {
   from( zipTree(it) )
  }
 }
}

… the problem here is, that gradle tells me the task is up-to-date, which, again, seems plausible since no input has been configured during its configuration phase.

The following SEEMS to work, but seems very wrong :S

task extractApi(type: Copy) {
 dependsOn configurations.api
 from {
  configurations.api.asFileTree.each {
   from( zipTree(it) )
  }
  // Don't include the actual archives themselves
  null
 }
 into file("${project.buildDir}/api/")
}

Any advice? :wink:

Thanks in advance!, Mike

1 Like

Thanks, Peter, that works perfectly! :slight_smile:

I’m curious: I don’t see an obvious implementation of “from” taking a single closure as argument in http://gradle.org/docs/current/dsl/org.gradle.api.tasks.Copy.html How comes this works? What am I missing?

Best regards, Mike

Just for the record: The specified solution needs a

dependsOn configurations.api

Because if it’s not present and if the configuration depends on another project in a multi-project build, that project doesn’t get build - resulting in missing dependency artifacts in the copy operation.

Correct. There are some cases (like this one) where Gradle cannot infer the task dependencies automatically.

It’s this one: http://gradle.org/docs/current/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:from(java.lang.Object...)

Note the sentence: “The given paths are evaluated as for Project.files()”. This is a common pattern in Gradle.

Peter,

Your code works for me but it only extracts one file at a time. If I have 7 native jars from dependecy archives that I would like to extract to their own directories. Do you know how to that in a loop?

dependencies {

runtime ‘net.java.dev.jogl:jogl-linux-amd64:1.1.1’,

‘net.java.dev.jogl:jogl-linux-i586:1.1.1’,

‘net.java.dev.jogl:jogl-macosx-ppc:1.1.1’,

‘net.java.dev.jogl:jogl-macosx-universal:1.1.1’,

‘net.java.dev.jogl:jogl-solaris-amd64:1.1.1’,

‘net.java.dev.jogl:jogl-solaris-i586:1.1.1’,

‘net.java.dev.jogl:jogl-solaris-sparc:1.1.1’,

‘net.java.dev.jogl:jogl-solaris-sparcv9:1.1.1’,

‘net.java.dev.jogl:jogl-windows-amd64:1.1.1’,

‘net.java.dev.jogl:jogl-windows-i586:1.1.1’

}

Thanks for your help in advance.

A side affect of this code, is that Gradle will resolve and download dependencies even if the task is not in the taskgraph. Is there a way to move the dependency resolution to task execution step?

I don’t know if this used to work, but on 1.9 the dependency is resolved even when the task is not executed. Can you update this snippet?

Still a bug? Can you verify this actually does what your comment says it will?

Works fine for me, both with 1.9 and 1.11. The configuration only gets resolved if Gradle decides that ‘extractApi’ will get executed. In that case, ‘from’ (and therefore also resolution of the configuration) gets triggered when the task execution graph is built, i.e. between configuration and execution phase.

Yes, i see it does work, just not with ‘gradle tasks’. Thank You.

Hi, where should this dependsOn be located? Who should depend on the configuration? Can you provide me with some more context please. THanks. martin

An idiomatic solution would look like this:

configurations {
    api
}
  dependencies {
    api 'somegroup:someArtifact:someVersion'
}
  task extractApi(type: Sync) {
    dependsOn configurations.api

    from { // use of closure defers evaluation until execution time
        configurations.api.collect { zipTree(it) }
    }
    into "$buildDir/api/"
}
5 Likes