How do I publish an artifact when I don't know the name until after it has built?


I have a Java project where the build process goes something like this:

  • compile and run tests as usual * call an external Java program that packages the class files and some other stuff into a zip with a specific structure (not a normal jar)

The external Java program (which is an executable jar called toolkit.jar) produces zips with a version number and timestamp in the file name, something like “”. I won’t know the name of the file that it produces until after the program has run.

This is what I have in my build script:

task exportBlade(type: Exec, dependsOn: test ) {
         executable "java"
     args '-jar'
     args "${project.rootDir}/build/toolkit.jar"
     args 'export'
     args "${project.projectDir}"
     args '-inputFiles'
     args sourceSets.main.output.classesDir
     args '-outputDir'
     args "${buildDir}/distributions"
       artifacts {
   bladeZip fileTree("${buildDir}/distributions").files.toArray()[0]
      build.dependsOn exportBlade

The idea is that when the exportBlade task runs it will call the Java program (toolkit.jar) and output the zip into the ${buildDir}/distributions directory. I don’t know what filename that zip will have so in my artifacts section I use a fileTree to just grab the first file in that directory. This relies on not having more than one file in the ${buildDir}/distributions directory, but that is taken care of elsewhere.

The problem is that Gradle evaluates my fileTree when it compiles the build script, at which point the ${buildDir}/distributions directory is empty. So it fails with this error:

FAILURE: Build failed with an exception.
  * Where:
Build file 'D:\Development\Motifs\fxmotif\server\build.gradle' line: 60
  * What went wrong:
A problem occurred evaluating project ':server'.
> 0

How can I define my artifact to be a file that is generated by a task that runs during the build when that file has a dynamic name that I don’t know at compile time?


This looks related so I’ll experiment with it:

It might be as simple as:

task exportBlade(type: Exec, dependsOn: test ) {
    executable "java"
    args '-jar'
      doLast {
        project.artifacts {
            bladeZip fileTree("${buildDir}/distributions").files.toArray()[0]

Worked like a charm. Thanks Daz.

Actually there is a bit of a problem with this. The project that builds these zips is actually a sub-project of a parent project. The parent project has a task that is supposed to gather all of these zips (there are multiple sub-projects) and deploy them to a location.

The relevant bits of the parent build looks like this:

configurations {
  dependencies {
   getBladeZips project(path: "mySubProject", configuration: "bladeZip")
  task gatherAndDeployBladeZips(dependsOn: configurations.getBladeZips) << {
   println "Deploying blade zips from sub-projects"

Unfortunately the gatherAndDeployBladeZips runs first, before running the sub-project build task which produces the zip. I think it’s because the sub-projects are now declaring their artifacts at run-time, so when this task is compiled it doesn’t think there are any artifacts in the getBladeZips configuration so it doesn’t need to do anything.

It works fine if I change the task definition to this:

task gatherAndDeployBladeZips(dependsOn: "mySubProject:build") << {

But that isn’t great because I now have to list my sub-projects twice, once in the dependencies section to add them to the getBladeZips configuration and then I have to list them again in the dependencies for the gatherAndDeployBladeZips task.

Is there a better way to get around this problem?