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.
}
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.
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"
}
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.
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: