I think in your case, the easiest thing for you to do is to drop the separate launcher jar, and add the Class-Path entry to your normal jar like this:
project('app:IntelliStat') {
apply plugin:'application'
version = 1.0
dependencies {
compile project(':api:itl')
compile 'net.sf.jasperreports:jasperreports:4.5.1'
compile 'org.springframework:spring:2.5.6.SEC01'
runtime 'commons-cli:commons-cli:1.2'
runtime 'commons-dbcp:commons-dbcp:1.3'
}
jar {
manifest {
attributes "Class-Path": configurations.runtime.files*.name.join(" ")
}
}
startScripts {
// clear up the classpath because the manifest has it.
classpath = jar.outputs.files
}
task copyDatasource(type: Copy) {//copy our <datasource>.xml into intellistat-datasource.xml
from('/datasource/')
into('src/main/resources/')
include(datasource + '.xml')
rename(datasource + '.xml', 'intellistat-datasource.xml')
}
processResources.dependsOn ':app:IntelliStat:copyDatasource'
mainClassName = "net.intellidata.intellistat.ReportRunner"
jar.baseName = 'intellistat'
distZip.baseName = 'intellistat'
}
I personally have all the “launcher” logic in a dedicated subproject without code or dependencies of it’s own, and subsequently i don’t suffer from these “timing” problems you are facing.
Why have a task ‘copyDatasource’ instead of configuring ‘processResources’? You shouldn’t copy stuff into a source directory (src/main/resources). The way that ‘launcherJar’ and ‘dependencies.runtime’ refer to each other seem circular. Not sure but this might be a problem.
The reason for the copyDatasource method is that we have multiple datasource files depending on which database we want our application to connect to. The specific datasource is defined as -Pdatasource=XXX at the command line. This is something we’re using in all of our database-dependent projects but if you have a better solution I’m eager to see it.
As to the circular dependency issue - do you have any suggestions? Every variation I’ve tried has fixed one part of this only to break something else
Rolf: I also tried adding a doLast {…} block around the manifest in the jar task, which gets the rest of the dependencies into the lib dir, but leaves me with an empty manifest again. Same with jar << {…}
Peter: Can you give me an example that would pull the datasource as copyDatasource does while still getting the other resources from the src/main/resources dir?
Thanks Peter, that seems to work, and will make my build scripts much nicer. Do you have any suggestions on the main issue I’m trying to resolve in this thread?
Any more ideas? The only other option I’ve found so far is to make a so-called “fat” jar and include the all the dependencies physically inside the jar, but that seems like a truly monumental hack… (not that I have that working, either)
The problem seems to be your hierarchical layout (itl being a subproject of api, etc).
I have made a small example based on your build file but with a flat project layout, and this seems to include all jars in your lib folder, and populate the manifest’s Class-Path properly: http://wikisend.com/download/853588/test.zip
Note: the zip file will be gone in a week
This does not mean you have to refactor your entire project’s folder structure: your projects can still live in subfolders, but then you need some logic in your settings.gradle file setting the apropriate folder for each project. Many projects do this, and in fact, gradle itself does this as well: https://github.com/gradle/gradle/blob/master/settings.gradle
I’m not sure if the problems you’ve encountered could be interpreted as a bug with hierarchical project layouts, since flat layouts seem to work just fine, but maybe one of the gradle devs can at least shed some light as to why a flat project layout seems to resolve dependencies a little bit different then hierarchical layouts:)
Rolf - Thanks, this looks promising. But I’m afraid i’m not terribly familiar with groovy so I’m not entirely sure what each of the statements in that settings.gradle means. (Not a scripting language guy, I’m rather fond of Java and its explicit variable definitions and strong typing).
Anyway, do you know of a place where I can go to get a better understanding of what is being done in that file so I can adapt it? Or perhaps you can give me a brief example?
If it helps, this is the contents of our settings.gradle file:
if you are not able or allowed to change the physical structure of the folders (i.e. move all folders under api/ and app/ up one level), then you need to do something like this:
include 'core-api', 'intellispec-api', 'itl-api', 'pnf-api', 'pricing-api', 'schema-api', 'sleuth-api', 'util-pi'
include 'DataC-app', 'DataExpress-app', 'DPU2-app', 'IntelliData-app', 'IntelliStat-app', 'IQ-app', 'IQE-app', 'ISManager-app', 'ITLDemo-app', 'PNFManager-app', 'PNGenerator-app'
rootProject.children.each {project ->
int dash = project.name.indexOf('-')
// grab 'api' or 'app' suffix
String suffix = project.name.substring(dash + 1)
String folder = project.name.substring(0, dash)
// inform gradle the project is located in suffix/folder, relative
// to the directory of this settings.gradle
project.projectDir = new File(new File(settingsDir, suffix), folder)
// debug - remove later ;)
println "${project.name} is located at ${project.projectDir}"
}
Note that this changes the name of each of your projects, so you need to update all project dependencies to use this new name.
Rolf - I implemented the change you recommended, but with no change in net behavior. As of right now I have a populated class-path in my manifest, but the lib dir is still missing a lot of my dependencies.
You said this earlier: “I personally have all the “launcher” logic in a dedicated subproject without code or dependencies of it’s own, and subsequently i don’t suffer from these “timing” problems you are facing.”
How does that look in your build script?
Also - what version of gradle are you using? maybe there’s a difference in behavior between whatever you’re using and milestone 9?
I tried a dedicated subproject in the minimal example i uploaded, and due to the project hierarchy it did not work either. Once i rearranged it to a flat layout, i got all dependencies in.
Can someone throw some light around this? I have the same issue going on … but could not get this working… we dont use startscripts or application… for our pacaking… is it because we have the class path manifest just mentions the name of the jar but not the actual class path ?? cause I do see manifest file inside the launcherJar… but those are not the correct paths from where gradle should pull all those?