How to configure dependencies that get extracted from other dependencies?

In an Android project, I have a build.gradle file like

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
      compile 'group-of-zip-file:name-of-zip-file:version-of-zip-file@zip'
}
  task installDependencies(type: Copy) {
    configurations.compile.resolvedConfiguration.resolvedArtifacts.each { artifact ->
        if (artifact.file.name.endsWith('.zip')) {
            from zipTree(artifact.file)
            into 'libs'
        }
    }
}
  preBuild.dependsOn installDependencies

This resolves a ZIP file as a dependency, extracting a JAR file into my Android project’s “libs” directory. The problem is that I need to call “resolvedArtifacts” in “installDependencies”, which triggers dependency resolution and calls fileTree(‘libs’). However, that does not yet match anything as the JAR has not yet been extracted to “libs”. What’s the best way to solve this? Can I somehow delay the fileTree() call until after extraction of the JAR, or trigger dependency resolution again?

1 Like

Use the the Project ‘copy’ method inside of a ‘’‘doLast’’’ block on the task. This will prevent the configuration from being resolved until the execution phase.

Sure, but that would also get rid of the Copy-tasks built-in up-to-date check, or? In other word, the “copy” method would always copy the files, even if they are already there, if I’m not mistaken.

Correct. This can be resolved using the 15.9. Skipping tasks that are up-to-date feature.

Which brings me to the next question: If I want to add an up-to-date check to my task, I need to define its outputs (and inputs). The outputs would be the contents of the ZIP file, relative to the “libs” directory. How am I able to get this list of files in an elegant way? The FileTree returned by zipTree() contains absolute (!) paths to some tmp directory where the ZIP is extracted. But what I’d need is the list of files contains in the ZIP (preferably without even extracting it). Also, I do not want to specifiy the whole “libs” directory as outputs, as that may contain other files than those from the ZIP.

Also, all that would not really solve the problem: In order to define the outputs of my task, I’d need to resolve the ZIP first in order to read its contained files, so I’d again need to trigger artifact resolution before my artifacts are extracted to the “libs” directory.

Is there a way to only trigger resolution of ZIP artifacts (well, other than maybe creating a separate configuration for all ZIP artifacts)?

OK, so here’s what I’m doing for now. I’m defining ZIPped artifacts in a separate configuration so I can resolve them independently of other artifacts already at configuration time:

configurations {
    zip {
        description = 'ZIPped artifacts that need to be extracted first.'
        visible = false
    }
}
  dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
    zip 'group-of-zip-file:name-of-zip-file:version-of-zip-file@zip'
}
  task unzipDependencies {
    configurations.zip.resolvedConfiguration.resolvedArtifacts.each { artifact ->
        inputs.file artifact.file
          def zipFile = new java.util.zip.ZipFile(artifact.file)
        zipFile.entries().each { entry ->
            if (!entry.isDirectory()) {
                outputs.file entry.getName()
            }
        }
    }
      doLast {
        inputs.files.each { zip ->
            copy {
                from zipTree(zip)
                into 'libs'
            }
        }
    }
}
  preBuild.dependsOn unzipDependencies

As I explicit set the inputs and outputs, the automatic clean task (cleanUnzipDependencies) is working fine now (expect that it does not delete empty directories). If this was a Copy task, cleanUnzipDependencies would delete all files in the output directory as already discussed in this forum, so this task definitely is an improvement.

However, unzipDependencies is still being called too late. Is there a way to call it before the “compile” configuration is resolved?

I ended up doing

// Execute the whole task already during configuration phase before compile dependencies get resolved.
tasks['unzipDependencies'].execute()

instead of

preBuild.dependsOn unzipDependencies

Not exactly nice, but it works.

Glad you got it working. Personally, I would try to keep the inputs defines as simple as possible, even if it means sometimes the task is executed unnecessarily. Even internal Gradle tasks have problems correctly detecting changes 100% of the time.

Now that I’ve learned over here that ‘task.execute()’ should never be called directly, I’m still looking for a good solution to this problem.