Why is the classpath in my manifest empty the first time I run my build?

I’m working on my first build script for a runnable jar, intending for it to run with it’s dependency jars in a folder next to it named the same except ending with “Libs”. The following works the second time on, but the first time (or if I delete the contents (jars) of the previously created ‘Libs’ folder) the classpath in the jar’s manifest in empty: "Class-Path: . ". I’m not sure why this is happening, I see this in my log, so I believe my tasks are executing in the proper order:

Tasks to be executed: [task ':compileJava', task ':processResources', task ':classes', task ':copyDependencies', task ':jar', task ':startScripts', task ':distZip']

Also, adding println statements to the beginning and ending of the tasks in question confirms the order:

09:45:53.273 [QUIET] [system.out] Starting copyDependencies
09:45:53.283 [QUIET] [system.out] Ending copyDependencies
09:45:53.283 [QUIET] [system.out] Starting jar
09:45:53.283 [QUIET] [system.out] jar task about to parse folder for jar files to include in manifest. Folder: C:\dev_tools\workspace\MyApp\build/libs/MyAppLibs
09:45:53.283 [QUIET] [system.out] Jars in lib folder:
09:45:53.283 [QUIET] [system.out]
activation-1.1.jar
//... Other jar file logging omitted
09:45:53.293 [QUIET] [system.out] Ending jar
apply from: '../BuildScripts/CommonBuild.gradle'
apply plugin: 'application'
apply plugin: 'java'
  group = 'org.whatever'
version = '1.0.0'
  description = "MyApp"
jar.archiveName = "${description}.jar"
sourceSets.main.java.srcDirs = ['src/main']
mainClassName = 'org.whatever.Driver'
def libFolderName = "${description}Libs"
def libFolderPath = "${buildDir}/libs/${libFolderName}"
  task copyDependencies(type: Copy) {
 dependsOn classes
 def libDir = new File(libFolderPath)
 if (!libDir.isDirectory()) //Create lib folder if it doesn't already exist.
  libDir.mkdir()
 from configurations.runtime
 into libFolderPath
}
  jar {
  dependsOn copyDependencies
 println "jar task about to parse folder for jar files to include in manifest. Folder: ${libFolderPath}"
 def libDir = new File(libFolderPath)
 def depJars = '. '
 println "Jars in lib folder:"
 libDir.eachFile {
  println "\t${it.getName()}"
  depJars += "${libFolderName}/${it.getName()} "
 }
 manifest {
  attributes 'Main-Class': mainClassName
  attributes 'Class-Path' : depJars
 }
}
  dependencies {
 //Omitted for brevity.
}

Any help would be much appreciated!!

The problem is that you are constructing the ‘depJars’ String during configuration time, during which the ‘copyDependencies’ task has not yet executed, so there are no files in the libs folder yet. It work during subsequent builds because the directory already exists. The simplest way to fix this is to iterate over the ‘runtime’ configuration, rather than the output of that task, since we effectively know at configuration time what files will get copied. You can also simplify your implementation considerably.

jar {

manifest {

attributes ‘Class-Path’ : “${(configurations.runtime.collect { “deps/$it.name” }).join(’ ') }”

}

dependsOn copyDependencies

}

You also don’t need to manually create the output directory for the ‘copyDependencies’ task. The ‘Copy’ task will take care of that for you.

Thanks for the reply, Mark! I tried you’re suggestion, setting the classpath like:

attributes 'Class-Path' : "${(configurations.runtime.collect { "${libFolderName}/$it.name" }).join(' ') }"

However now “log4j-1.2.17.jar” is the only dependency that gets added. It’s like the runtime configuration isn’t fully initialized or something at that time.

What’s really odd is that the code below “works” as is (with the exception of the first run issue), however if I uncomment the two commented-out lines (which just reads the contents of the runtime config and prints it to the console while the actual classpath variable (depJars) remains unchanged), it doesn’t work and produces the behavior above. It’s like reading the configuration changes it’s contents.

jar {
  println "Starting jar"
 println "jar task about to parse folder for jar files to include in manifest. Folder: ${libFolderPath}"
 def libDir = new File(libFolderPath)
 def depJars = '. '
 println "Jars in lib folder:"
 libDir.eachFile {
  println "\t${it.getName()}"
  depJars += "${libFolderName}/${it.getName()} "
 }
   // def rtj = configurations.runtime.collect
{it.getName()}.join(' ')
// println "rtj = ${rtj}"
 println "Class-Path : ${depJars}"
 manifest {
  attributes 'Main-Class': mainClassName
  attributes 'Class-Path' : depJars
 }
 dependsOn copyDependencies
 println "Ending jar"
}
13:19:49.268 [QUIET] [system.out] rtj = log4j-1.2.17.jar
13:19:49.268 [QUIET] [system.out] Class-Path : .
 13:19:49.314 [QUIET] [system.out] Ending jar

Re-running the build subsequent times doesn’t add the missing jars either. I’m sure I’m missing something fundamental to Gradle, but this has me baffled.

Thanks again!

Also, if I delete the Libs folder and comment out the part of the build that tests if the Lib folder exists and creates it if not, my build throws an exception:

Caused by: java.io.FileNotFoundException: C:\dev_tools\workspace\MyApp\build\libs\MyAppLibs

FYI, it looks like I’m running Gradle 2.2.1

Should work in 2.2.1 as well.

into “$buildDir/libs/${description}Libs”