gradle subproject dependencies as list of other subprojects - gradle

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
}
}
}
}

Related

Print sorted list of dependencies for all projects in Gradle build

I want to create a complete list of dependencies for all projects in my Gradle build to track changes over time.
For this purpose, both the list of projects and the list of all dependencies (direct and transitive) must be sorted. Without both, the diff between releases is useless.
My first attempt was
fun listDependencies(project: Project, configName: String) {
val config = project.configurations.findByName(configName) ?: return
println()
println("$configName of module ${project.name}:")
config.allDependencies.map {
" ${it.group}:${it.name}:${it.version}"
}.sorted().forEach {
println(it)
}
}
fun listDependencies(project: Project) {
listDependencies(project, "compileClasspath")
listDependencies(project, "testCompileClasspath")
}
/** Create sorted list of dependencies per module and only for the compileClasspath and testCompileClasspath */
task("listDependencies") {
doLast {
println("Dependency list per project")
allprojects.sortedBy { it.name }
.forEach {
listDependencies(it)
}
}
}
In the output, most versions are null and all transitive dependencies are missing.
I had to force dependency resolution by changing the config.allDependencies above to
config.resolvedConfiguration.resolvedArtifacts.map {
" ${it.moduleVersion}"
}.sorted().forEach {
println(it)
}
This works but gives me a warning that I shouldn't call resolvedConfiguration (Resolving unsafe configuration resolution errors).
What do I have to change to run this task once after the configuration phase only when I ask for it by running Gradle with ./gradlew listDependencies?
We don't need this task in every build.

Configuration inheritance and resolution

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()
}
}

Gradle multi project build with applied compile project() dependency for all subprojects

I have a multi project gradle build and I would like to apply one of the subprojects as dependecy for all other subprojects.
That is, if I specify:
dependencies {
compile project(":my-shared-subproject")
}
in all relevant build.gradle files for each subproject, it works. I if instead do:
subprojects { project ->
dependencies {
if(project.name != 'my-shared-subproject') {
compile project(":my-shared-subproject")
}
}
}
Gradle gets angry and throws the followin error:
A problem occurred evaluating root project 'my-project'.
> Could not find method call() for arguments [:my-shared-subproject] on project ':my-other-subproject' of type org.gradle.api.Project.
Am I wrong in thinking this should be possible? If not - what am I doing wrong? :)
Remove the project parameter from subprojects
subprojects {
dependencies {
if(project.name != 'my-shared-subproject') {
compile project(":my-shared-subproject")
}
}
}

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