Get the source and destination directories within eachFile {}

I would like to override the behaviour of a copy task, and sign a jar while copying it. For this I’m using the ant.signjar task and need the paths of the source and destination. But the FileCopyDetails only give me relative names of the files. I found the destinationDir (I’m guessing from the Copy task), but I can’t find any way to get the source directory. For example:

shadowJar {
  baseName = jar.baseName
  classifier = jar.classifier
}

task signJar(type: Copy) {
  into "${buildDir}/signed"
  dependsOn shadowJar
  from(shadowJar) {
    eachFile { fileCopyDetails ->
        def sourceFile = fileCopyDetails.relativeSourcePath.getFile(new File(buildDir, "libs"))
        def destinationFile = fileCopyDetails.relativePath.getFile(destinationDir)
        println "IT2: ${sourceFile} -> ${destinationFile}"
        ant.signjar(signerSettings << [jar: sourceFile, signedjar: destinationFile])
        // and exclude?
    }
  }
}

Is there a better way to write a task that generates a singed jar from another jar?

Regards
Levon

You can get the full path via fileCopyDetails.file.path.

That said, I’m not sure why this is being implemented as a Copy task, seeing how you are only ever copying a single file. You could just as well implement this as an ad-hoc task.

task signJar {
    inputs.file shadowJar
    outputs.file "$buildDir/signed/${shadowJar.archiveName}"
    doLast {
        ant.signjar(jar: shadowJar.archivePath, signedjar: "$buildDir/signed/${shadowJar.archiveName}")
    }
}

I ended up writing a custom task that inherits from AbstractArchiveTask, since I wanted it to be recognized by gradle as an archive.

I had a similar problem though, since I had to implement a copy action. Ended up using a CopySpecResolver (mainSpec.buildRootResolver()) and then resolver.source.singleFile as source and archivePath as destination.

We should probably make all this stuff rely on a marker interface rather than a base class.

Disclaimer, this is all internal stuff and likely to change without notice. I’m not sure this is worth the convenience of having Gradle automatically treat your task as an archive.

Your ad-hoc task seemed nicer. How would I do to use it and let Gradle know that it generated an archive? So that other projects that depend on this project used the archive automatically?

Are you currently using artifacts { } for this?

Yes:

artifacts {
    archives signJar
}

Yeah, so if you simply implement this as a custom task that doesn’t extend AbstractArchiveTask you would just expose a property on that task which is the file it creates, let’s call it outputFile.

artifacts {
    archives (signJar.outputFile) {
        builtBy signJar
    }
}

This adds outputFile to the ‘archives’ configuration as well as instructs Gradle that the signJar task is responsible for creating this archive so all the task dependencies get setup exactly the same as if this was an AbstractArchiveTask.

1 Like

That’s great! Thank you.