Fixing broken module descriptors in multi-project build


(Russ Egan) #1

Got a multi-project build where many modules depend on jboss libraries. A few of these modules have broken descriptors. For example, one library creates a circular dependency on itself, which causes Gradle to stack overflow, and another depends on gnu getopts, but specifies the wrong group name.

I know how to fix these problems in a single build file (through exclusions), but I have to redo it in each subproject.

I also know how to extract the declaration out into a helper method defined in the root project. E.g.:

apply from: ‘…/common.dependencies.gradle’

addCompileJbossDependency()

(the messiness of declaring the right exclusions, and maybe adding the additonal libraries is done in the method).

But I was wondering if there was a clean way to have the root project silently modify all dependencies of all subprojects which declare the offending dependencies. Like, in my subproject, I could just declare the dependency the normal way:

compile ‘org.jboss:jboss-security:1.0’

and in the root project, do something like:

allprojects {

if(dependencies.contains(‘org.jboss:jboss-security:1.0’) {

getDependency().exclude(‘org.gnu:getopts:1.0’)

dependencies.compile ‘gnu.getopts:java-getopts:1.0’ }

Something like that. I guess akin to Maven’s dependency management, where I can modify all usages of a dependency in one place.


(Merlyn Albery-Speyer) #2

Take a look at Gradle’s build - https://github.com/gradle/gradle/blob/master/build.gradle. The convention in use there is defining a map called libraries, and referencing that in subprojects. You can also set global excludes. E.g.

Rootproject:

libraries = [
  jbossSecurity: 'org.jboss:jboss-security:1.0'
]
  subprojects {
  configurations {
    all*.exclude group: 'org.gnu', module: 'getopts'
  }
}

Subproject:

dependencies {
  compile libraries.jbossSecurity
}

(Russ Egan) #3

Digging around, I found a more elegant solution to the exclusion:

root project, or include:

def jbossSecurity = ['org.jboss:jboss-security:1.0', { exclude group: 'org.gnu' }]

subproject:

dependencies {
  compile jbossSecurity
}

Limits the scope of the exclusion to just the broken module.

Ideally though, I’d rather replace it than just exclude it. It’s a valid dependency, but it should be gnu.getopt instead of org.gnu. Haven’t found a way to do that without using ClientModules, which would mean I’d have to redefine all the dependencies (pain).

Sort of looking for either a “replace” equivalent to “exclude”, or…or access to the dependency graph as its build maybe? Though you’d need access as it’s resolved, step by step, to fix problems during resolution (after graph is built it’s too late).


(Adam Murdoch) #4

We plan to provide some way to substitute/add/remove dependencies at resolve time. This will provide a general solution that we can build on for much improved conflict management, and for fixing broken dependency meta-data (amongst other things). There will be some convenience methods in the DSL to add rules like: replace any dependency on ‘org.gnu:getopts:$version’ with a dependency on ‘gnu.getopts:java-getopts:$version’, and this will take effect regardless of how the dependency ends up in the dependency graph. The substitution will also happen in the correct place in the dependency graph, so that it shows up correctly in the output of ‘gradle dependencies’

We need to get a bit further along with decoupling the repository dsl from Ivy, and with some internal restructuring that we’re currently doing, before we can tackle this.


(Russ Egan) #5

That sounds perfect. Can’t wait :slight_smile:


(Karl Klashinsky) #6

So this is pretty well what I want/need, the ability to “fix” dependencies in a multi-project build.

Specifically, I want to be able to look into a project’s dependencies, and for any that are of the form project(":foo"), I might want to switch it to a repo specifier (e.g., “com.fubar.proj:foo:1.0”), if the actual source for “:foo” isn’t there.

Suggestions?