I have written the following task, which extracts all the compile dependencies for each of my sub-projects and puts them in a per sub-project directory:
task exportCompileLibs << {
subprojects.each { iSubProject ->
iSubProject.configurations.findAll{it.name == "compile"}.each{ jConfig ->
println "copying compile libs for ${iSubProject.name}..."
copy {
into "${iSubProject.buildDir}/gradle-lib-export"
from jConfig
eachFile {println it.name}
}
}
}
}
I'd like to extend this to also export the source artifacts that Gradle does already know about (I can see the source jars in the cache directory), I just can't figure out how to use the object model to get a handle to them.
The IDEA and Eclipse plugins seem to be able to do this (they point the project files they build directly into the gradle cache), but I can't figure out how to do it - and looking at the IDE plugin source code, it looks... tricky. I'm hoping there's something obvious that I'm missing in the gradle DSL or API.
Anyone got any ideas?
For anyone looking for at least an interim solution to this, the following seems to be doing pretty much exactly what I want at the moment.
You have to apply the IDEA plugin to the build.gradle file for each project you want to export the dependencies for:
apply plugin: 'idea'
And then define this task:
task exportDependencies << {
def deps = project.extensions.getByType(IdeaModel).module.resolveDependencies()
copy {
from deps*.classes.file
into "${buildDir}/gradle-lib-export/libs"
}
copy {
from deps*.sources.file
into "${buildDir}/gradle-lib-export/sources"
}
}
And here's my horrific hack so I don't have to apply the plugin for each sub-project:
task exportDependencies(description: "export project dependency jars") << {
subprojects.each { Project iSubProject ->
String target = "${iSubProject.buildDir}/gradle-lib-export"
IdeaPlugin ideaPlugin = new IdeaPlugin()
ideaPlugin.apply(iSubProject)
Set<Dependency> deps = ideaPlugin.model.module.resolveDependencies()
println "exporting dependencies for $iSubProject.name into $target"
copy {
from deps*.classes.file
into "${target}/libs"
eachFile { println "lib -> $it.name" }
}
copy {
from deps*.sources.file
into "${target}/sources"
eachFile{ println "source -> $it.name" }
}
}
}
+10 points for not cluttering my build task list with stuff I don't want, -several million points for ewwwww.
There isn't currently a simpler way than what the IDE plugins are doing. This will hopefully change in the future.
Related
I have a simple multi-module project and I'd like to create a distribution from it. I roughly would like the distro to look like
foo/
bar/
baz/
For each module, I'd like to declare that their artifacts (and dependencies) go into one of foo, bar, or baz directories in the distro.
The obvious thing doesn't seem to work, and the Gradle docs are vague about how exactly artifacts + configurations work, so I'm not sure what I've done wrong.
I have in the root build.gradle
configurations {
foo
bar
baz
}
Then in moduleA I put for its artifacts:
artifacts {
foo jar
}
And so on, for each module, its artifacts are set to the desired configuration.
These configurations are listed if I print them out ala configurations.each {c -> println(c.name) }, so they're created correctly, but if I try to iterate over the files or allArtifacts of my custom configurations, they're empty.
I'd previously done something like this by creating ancillary modules whose dependencies would be listed like:
dependencies {
foo "com.mypackage.group:module:1.0"
}
And then another module for bar, and another for baz and so on. This seemed to work but it was a hassle to maintain. Using custom configurations along with artifacts seemed like a more maintainable solution for this.
I think you are interested in the getArtifacts()
Here's an example
// generated with gradle init --type java-library
apply plugin: 'java'
repositories {
jcenter()
}
dependencies {
compile 'org.slf4j:slf4j-api:1.7.21'
testCompile 'junit:junit:4.12'
}
configurations {
foo {
extendsFrom compile
}
}
artifacts {
foo jar
}
task showMe << {
println "Files:"
configurations.foo.files.each { println " $it" }
println "Artifacts: "
configurations.foo.artifacts.each { println " $it" }
}
Output:
:showMe
Files:
/home/alpar/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.21/139535a69a4239db087de9bab0bee568bf8e0b70/slf4j-api-1.7.21.jar
Artifacts:
ArchivePublishArtifact_Decorated alpar:jar:jar:
You could potentially simplify it even more by referring to the projects tasks directly ( considering that foo bar etc are projects )
task distribution(type: Zip) {
from project(':foo').jar { into "foo" }
}
or
task distribution(type: Zip) {
('foo', 'bar').each { p ->
from project(":$p").jar { into p }
}
}
for short.
It seems like the publish PublishArtifact was not ment to be used with a Zip task, as it's non trivial to connect.
You could also loop trough all sub projects that have some custom parameter defined for the name of the task to pick up to make it more self contained in the sub-projects.
Make sure to also include some dependencies to make your task work even if called after a complete clean, or on a fresh checkout.
In my Gradle build script I want to import a ZIP dependency that contains static analysis configuration (CheckStyle, PMD etc.) and then "apply from" the files in that ZIP. When anyone runs the "check" task, my custom static analysis configuration should be used then.
I've tried the somewhat convoluted solution below, but I can't get it to work. The files are retrieved and unpacked into the "config" directory, but "apply from" does not work - Gradle complains it cannot find the files; I assume this is due to "apply from" being run during the build configuration phase.
Is there a simpler way to do this?
repositories {
maven { url MY_MAVEN_REPO }
}
configurations {
staticAnalysis {
description = "Static analysis configuration"
}
}
dependencies {
staticAnalysis group:'my-group', name:'gradle-static-analysis-conf', version:'+', ext:'zip'
}
// Unzip static analysis conf files to "config" in root project dir.
// This is the Gradle default location.
task prepareStaticAnalysisConf(type: Copy) {
def confDir = new File(rootProject.projectDir, "config")
if (!confDir.exists()) {
confDir.mkdirs()
}
from {
configurations.staticAnalysis.collect { zipTree(it) }
}
into confDir
apply from: 'config/quality.gradle'
}
check.dependsOn('prepareStaticAnalysisConf')
You are perfectly right: Gradle runs apply during evaluation phase, but the prepareStaticAnalysisConf was not executed yet and the archive is not unpacked.
Instead of a task, just write some top-level code. It should do the trick. Also, you'd better use the buildscript level dependency, so that it is resolved before script is executed.
Here is the full script
buildScript {
repositories {
maven { url MY_MAVEN_REPO }
}
dependencies {
classpath group:'my-group', name:'gradle-static-analysis-conf', version:'+', ext:'zip'
}
}
def zipFile = buildscript.configurations.classpath.singleFile
copy {
from zipTree(it)
into 'config'
}
apply from: 'config/quality.gradle'
I'm trying to get Gradle to handle the deployment of a very large Web application. In the past when we used Ant, instead of creating a very large .war file, we would simply assemble all the code in one folder--libraries, .jsp's etc--and then scp them to the deployment destination. This would speed deployment since we would be moving only the files that changed.
I'm having trouble trying to do this with Gradle, however. Using the War plugin creates an actual .war file, which we don't want. I've tried simply creating a task that depends on 'classes' and that generates the necessary classes and resources folders. However, where are the library dependencies? How can I get this all in one place so I can do an scp?
Current build:
apply plugin: "java"
apply plugin: "maven"
apply plugin: 'war'
apply plugin: 'eclipse-wtp'
sourceCompatibility = 1.6
targetCompatibility = 1.6
repositories {
maven {
url 'http://buildserver/artifactory/repo'
}
}
sourceSets {
main {
java {
srcDir 'src'
}
resources {
srcDir 'src'
}
}
}
webAppDirName = 'web'
dependencies {
compile 'antlr:antlr:2.7.6'
compile 'antlr:antlr:2.7.7'
*** etc ***
}
task deploystage(dependsOn: 'classes') << {
println 'assemble and scp code here'
}
your right Opal ... no easynway to use the War plugin for this ... although it would be nice if it had a createExplodedWar option. I just used the Jar plugin methods.
The .jars were available, along with other things in war.classpath.files:
gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
project.ext.set("allclasspathjars", files(war.classpath.files))
}
I then when through these to separate out the .jars from the other stuff:
task deployJars(dependsOn: 'classes') << {
FileUtils.cleanDirectory(new File("web/WEB-INF/lib/"))
project.allclasspathjars.each {File file ->
if(file.name.endsWith(".jar")) {
println "Including .jar: " + file.name
org.apache.commons.io.FileUtils.copyFile(file, new File("web/WEB-INF/lib/" + file.name))
}
}
After this I could copy all the classes & resources into the .war structure:
task createExplodedWar(type: Copy, dependsOn: 'deployJars') {
from 'build/classes/main'
from 'build/resources/main'
into 'web/WEB-INF/classes'
}
Finally used Gradle SSH Plugin to push it up to tomcat and restart the server.
All good.
I've hacked together combination of build.gradle and settings.gradle below for creating an ad-hoc multi-module project out of several single-module projects (e.g., an application and all of its dependencies, or a shared library and everything that uses that library).
settings.gradle:
// find all subprojects and include them
rootDir.eachFileRecurse {
if (it.name == "build.gradle") {
def projDir = it.parentFile
if (projDir != rootDir) {
include projDir.name
project(":${projDir.name}").projectDir = projDir
}
}
}
build.gradle::
// Make sure we've parsed subproject dependencies
evaluationDependsOnChildren()
// Map of all projects by artifact group and name
def declarationToProject = subprojects.collectEntries { p -> [toDeclaration(p), p] }
// Replace artifact dependencies with subproject dependencies, if possible
subprojects.each { p ->
def changes = [] // defer so we don't get ConcurrentModificationExceptions
p.configurations.each { c ->
c.dependencies.each { d ->
def sub = declarationToProject[[group:d.group, name:d.name]]
if (sub != null) {
changes.add({
c.dependencies.remove(d)
p.dependencies.add(c.name, sub)
})
}
}
}
for (change in changes) {
change()
}
}
This works, but it's hard to share -- if somebody else wants to do something similar they have to copy my *.gradle files or cut and paste.
What I'd like to do is take this functionality and encapsulate it in a plugin. The build.gradle part looks easy enough to do in the plugin apply() method, but it seems like the list of subprojects is already set in stone before the plugin gets a chance at it. Is there any way to get in earlier in the build process, e.g. by applying to something other than Project? Or should I resign myself to giving my plugin a task for overwriting settings.gradle?
Solution: Per Peter Niederweiser's answer, I moved the code above into two plugins, one to be called from settings.gradle and the other to be called from build.gradle. In settings.gradle:
buildscript {
repositories { /* etc... */ }
dependencies { classpath 'my-group:my-plugin-project:1.0-SNAPSHOT' }
}
apply plugin: 'find-subprojects'
And in build.gradle:
buildscript {
repositories { /* etc... */ }
dependencies { classpath 'my-group:my-plugin-project:1.0-SNAPSHOT' }
}
evaluationDependsOnChildren()
apply plugin: 'local-dependencies'
Note that calling the plugin from settings.gradle doesn't work in Gradle 1.11 or 1.12 but does work in Gradle 2.0.
You'd need to apply a plugin in settings.gradle, which I believe is supported in recent versions.
We have one main project and two subprojects. One of the subprojects is the playframework which has a "unique" build structure. How can I override the source directories BUT only for that one subproject such that all other projects are using the standard layout of source directories src/main/java, etc.
I tried the first answer which is not working and for my directory structure
stserver
build.gradle (1)
project1
webserver
build.gradle (2)
The 2nd gradle file is this
sourceSets.main{
java.srcDirs = ['app']
}
task build << {
println "source sets=$sourceSets.main.java.srcDirs"
}
When I run this, it prints out stserver/app as my srcDir instead of stserver/webserver/app???? What am I doing wrong here?
thanks,
Dean
Please have a look at the docs Peter has suggested. I have a ready build.gradle that I have working with Play Framework 2.0~, so I'll share it here in hope you'll find some useful setup tips.
My project structure:
+- master/
+- build.gradle <-- contains common setup,
and applies 'java' plugin to all subprojects
+- ui/ <-- project using Play framework
+- build.gradle <-- excerpt from this file is posted below
The excerpt from build.gradle
repositories{
maven{
//Play dependencies will be downloaded from here
url " http://repo.typesafe.com/typesafe/releases"
}
}
//Optional but useful. Finds 'play' executable from user's PATH
def findPlay20(){
def pathEnvName = ['PATH', 'Path'].find{ System.getenv()[it] != null }
for(path in System.getenv()[pathEnvName].split(File.pathSeparator)){
for(playExec in ['play.bat', 'play', 'play.sh']){
if(new File(path, playExec).exists()){
project.ext.playHome = path
project.ext.playExec = new File(path, playExec)
return
}
}
}
throw new RuntimeException("""'play' command was not found in PATH.
Make sure you have Play Framework 2.0 installed and in your path""")
}
findPlay20()
configurations{
//Configuration to hold all Play dependencies
providedPlay
}
dependencies{
providedPlay "play:play_2.9.1:2.0+"
//Eclipse cannot compile Play template, so you have to tell it to
//look for play-compiled classes
providedPlay files('target/scala-2.9.1/classes_managed')
//other dependencies
}
sourceSets.main{
java.srcDirs = ['app', 'target/scala-2.9.1/src_managed/main']
//Make sure Play libraries are visible during compilation
compileClasspath += configurations.providedPlay
}
//This task will copy your project dependencies (if any) to 'lib'
//folder, which Play automatically includes in its compilation classpath
task copyPlayLibs(type: Copy){
doFirst { delete 'lib' }
from configurations.compile
into 'lib'
}
//Sets up common play tasks to be accessible from gradle.
//Can be useful if you use gradle in a continuous integration
//environment like Jenkins.
//
//'play compile' becomes 'gradle playCompile'
//'play run' becomes 'gradle playRun', and so on.
[ ['run', [copyPlayLibs]],
['compile', [copyPlayLibs]],
['clean', []],
['test', []],
['doc', [copyPlayLibs]],
['stage', [copyPlayLibs]] ].each { cmdSpec ->
def playCommand = cmdSpec[0]
def depTasks = cmdSpec[1]
task "play${playCommand.capitalize()}" (type: Exec,
dependsOn: depTasks,
description: "Execute 'play ${playCommand}'") {
commandLine playExec, playCommand
}
}
//Interate playClean and playCompile task into standard
//gradle build cycle
clean.dependsOn "playClean"
[compileScala, compileJava]*.dependsOn "playCompile"
//Include Play libraries in Eclipse classpath
eclipse {
classpath {
plusConfigurations += configurations.providedPlay
}
}
Note: I have just extracted the above from an existing bigger gradle file, so it might be missing some things, so no guarantees:) Hope it's useful anyway. Good luck.