Configuration inheritance and resolution - gradle

I have some trouble with gradle and I don't know why this doesn't work:
Project A with 2 sub-project B and C
B and C have a configuration named masterConfiguration and superConfiguration (who extend masterConfiguration)
I add some dependencies in both.
When I do this:
configurations.superConfigurations.resolvedConfiguration.files
All is fine, and I have all files from superConfiguration and masterConfiguration.
Now, the problem.
I create a configuration (projectAConfiguration) in the project A (the rootProject).
This configuration extends superConfiguration from B and C.
I add no new dependencies in this one.
If I do this:
configurations.projectAConfiguration.resolvedConfiguration.files
I have nothing. I don't understand why?
settings.gradle =>
include 'B'
include 'C'
build.gradle =>
configurations {
projectAConfiguration
}
def rootConfiguration = configurations.projectAConfiguration
subprojects {
configurations {
masterConfiguration
superConfiguration {
extendsFrom masterConfiguration
}
}
rootConfiguration.extendsFrom configurations.superConfiguration
dependencies {
masterConfiguration 'group:artifactid:version'
superConfiguration 'anotherGroup:anotherArtifactid:version'
}
//ALL IS OK
println configurations.superConfiguration.resolvedConfiguration.files
}
//NOT OK
println configurations.projectAConfiguration.resolvedConfiguration.files

I have solve my problem with this solution :
Add task on all subproject.
task resolveMyConf {
doLast {
it.ext.confResolve = it.configurations.superConfiguration..resolvedConfiguration.files
}
}
and in the project A
task resolveAllConf {
suprojects.each {
dependsOn it.resolveMyConf
}
doLast {
//and now, we can collect all task result
}
}
I don't know if it's good, maybe there is a better solution. But it works.

You can get the configuration of a sub-project just by referencing the project in the same way as you would declare a dependency to it. You don't need to create another configuration that extends it.
For example (Groovy DSL):
// Root project A
task printConfigurationB {
doLast {
println project("B").configurations.superConfiguration.resolve()
}
}

Related

gradle subproject dependencies as list of other subprojects

How can I get list of a subproject dependecies as list of Project object?
subproject.dependencies gives list of DependencyHandler objects. And I can't find how extract Project from DependencyHandler.
After a short have found solution myself :)
subprojects {
configurations.all {
allDependencies.each {
if (it instanceof ProjectDependency) {
println it.dependencyProject
}
}
}
}

Trying to get the group name and version of my build.gradle dependencies in a custom gradle task

I'm trying to do something which I feel should be relatively straightforward but nothing on the internet seems to be giving me what I'm after.
Basically, I want to take the compile/testCompile dependencies from my build.gradle (I don't want any of the sub-dependencies - just literally as they are in the file), and for each one I want to do something with the group name, name and version. Let's just say I want to print them.
So here is my build.gradle:
dependencies {
compile 'org.spring.framework.cloud:spring-cloud-example:1.0.6'
compile 'org.spring.framework.cloud:spring-cloud-other-example:1.1.6'
testCompile 'org.spring.framework.cloud:spring-cloud-example-test:3.1.2'
}
task printDependencies {
//some code in here to get results such as...
// org.spring.framework.cloud spring-cloud-other-example 1.1.6
}
Thanks all.
To iterate over all you dependencies, you can itareate over all the configurations and the over all configuration dependencies. Like so:
task printDependencies {
project.configurations.each { conf ->
conf.dependencies.each { dep ->
println "${dep.group}:${dep.name}:${dep.version}"
}
}
}
If you need exactly configuration dependencies, you can get them separately:
task printDependencies {
project.configurations.getByName('compile') { conf ->
conf.dependencies.each { dep ->
println "${dep.group}:${dep.name}:${dep.version}"
}
}
project.configurations.getByName('testCompile') { conf ->
conf.dependencies.each { dep ->
println "${dep.group}:${dep.name}:${dep.version}"
}
}
}
Or modify the first example, by adding condition to check conf.name

Gradle sourceSet depends on another sourceSet

This Question is similar to Make one source set dependent on another
Besides the main SourceSet I also have a testenv SourceSet.
The code in the testenv SourceSet references the main code, therefor I need to add the main SourceSet to the testenvCompile configuration.
sourceSets {
testenv
}
dependencies {
testenvCompile sourceSets.main
}
This does not work, because you cannot directly add sourceSets as dependencies. The recommended way to do this is:
sourceSets {
testenv
}
dependencies {
testenvCompile sourceSets.main.output
}
But this does not work correctly with eclipse, because when I clean the gradle build folder, eclipse can't compile anymore, since it depends on the gradle build.
Also if I change main code I'd have to rebuild the project in gradle for the changes to take effect in eclipse.
How do I declare the dependencies correctly?
EDIT:
This
sourceSets {
testenv
}
dependencies {
testenvCompile files(sourceSets.testenv.java.srcDirs, sourceSets.testenv.resources.srcDirs)
}
works for the main source, but because I now reference the .java files I am missing generated classes from the Annotation-Processor :(
So after all this is the way to go:
sourceSets {
testenv
}
dependencies {
testenvCompile sourceSets.main.output
}
To make it work correctly with eclipse you have to manually exclude all sourceSet outputs from the eclipse classpath.
This is ugly, but it works for me:
Project proj = project
eclipse {
classpath {
file {
whenMerged { cp ->
project.logger.lifecycle "[eclipse] Excluding sourceSet outputs from eclipse dependencies for project '${project.path}'"
cp.entries.grep { it.kind == 'lib' }.each { entry ->
rootProject.allprojects { Project project ->
String buildDirPath = project.buildDir.path.replace('\\', '/') + '/'
String entryPath = entry.path
if (entryPath.startsWith(buildDirPath)) {
cp.entries.remove entry
if (project != proj) {
boolean projectContainsProjectDep = false
for (Configuration cfg : proj.configurations) {
boolean cfgContainsProjectDependency = cfg.allDependencies.withType(ProjectDependency).collect { it.dependencyProject }.contains(project)
if(cfgContainsProjectDependency) {
projectContainsProjectDep = true
break;
}
}
if (!projectContainsProjectDep) {
throw new GradleException("The project '${proj.path}' has a dependency to the outputs of project '${project.path}', but not to the project itself. This is not allowed because it will cause compilation in eclipse to behave differently than in gradle.")
}
}
}
}
}
}
}
}
}

How should I add a project dependency to a limited set of subprojects?

I would like to add a certain project dependency to various subproject starting with a specific name.
I tried this
subprojects.findAll { project -> project.name.startsWith("myproject-sample") }.each { project ->
dependencies {
//compile project(":myproject-core")
}
}
but it gives
A problem occurred evaluating root project 'myproject'.
> Could not find method call() for arguments [:myproject-core] on project ':myproject-sample-hello-world'.
When I do this
subprojects {
dependencies {
compile project(":myproject-core")
}
}
it seems to work. But it adds the dep to all subprojects.
How should I add a project dep to a limited set of subprojects?
A clean solution is:
def sampleProjects = subprojects.findAll { it.name.startsWith("sample") }
configure(sampleProjects) {
dependencies {
compile project(":myproject-core")
}
}
Or:
subprojects {
if (project.name.startsWith("sample")) {
dependencies {
compile project(":myproject-core")
}
}
}
Both snippets assume that the sample projects have already had the java plugin applied (otherwise add apply plugin: "java" before the dependencies block).
The subprojects method delegates to an instance of the Project interface for each subproject, which is why your second example works (Project has a method called dependencies()). The each method however is simply passed a Project object as an argument. You then need to call the dependencies() method on that object. This requires a simple syntactical change.
subprojects.findAll { project ->
project.name.startsWith("myproject-sample")
}.each { project ->
project.dependencies {
compile project(":myproject-core")
}
}

Can a Gradle plugin modify the list of subprojects in a multi-module project?

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.

Resources