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.