19

Using Gradle 1.0 milestone 8.

My project uses slf4j+Logback for logging, so I want to prevent any transitive deps on log4j from polluting my classpath. Thus, I added a global exclusion, like so:

configurations {
    all*.exclude group: "log4j", module: "log4j"
}

However, I am using a test library (hadoop-minicluster) which has a runtime dependency on log4j, so I now need to allow a log4j dependency for my test runtime. I tried adding a direct dependency on log4j:

testRuntime group: "log4j", name: "log4j", version: "1.2.15"

and editing my exclusion code (a bit of a hack):

configurations.findAll {!it.name.endsWith('testRuntime')}.each { conf ->
    conf.exclude group: "log4j", module: "log4j"
}

But this does not work. Adding the exclusion to the testCompile conf automatically adds it to all inheriting configurations as well, including testRuntime. And it seems that this exclusion overrides even the explicit dependency that I added.

It appears that this is expected behaviour for Gradle. From the docs:

If you define an exclude for a particular configuration, the excluded transitive dependency will be filtered for all dependencies when resolving this configuration or any inheriting configuration.

So is there any other way to do what I want to achieve?

Ideas:

  • Create a new conf myTestRuntime that does not extend from testCompile, and use that for my test classpath.
    • But then I have to duplicate all dependencies for both testCompile and myTestRuntime.
  • Remove config-level exclusions. For all confs apart from testRuntime, loop through dependencies and manually remove log4j (or add a dep-level exclusion on log4j).
    • Is this even possible? Configuration.allDependencies is read-only.
Tunaki
  • 132,869
  • 46
  • 340
  • 423
Chris B
  • 9,149
  • 4
  • 32
  • 38

3 Answers3

10

For now I have managed to work around the problem, but I still welcome any better solutions.

Here is what I ended up doing:

  • Add a new configuration just for log4j:

    log4j(group: 'log4j', name: 'log4j', version: '1.2.15') {
        transitive = false
    }
    
  • Leave the config-level exclusion for all configurations apart from that one:

    configurations.findAll {!it.name.endsWith('log4j')}.each { conf ->
        conf.exclude group: "log4j", module: "log4j"
    }
    
  • Add the log4j configuration to my tests' classpath:

    test {
        classpath += configurations.log4j
    }
    

This way we can get log4j.jar onto the classpath, even though it is excluded from the testRuntime configuration.

Chris B
  • 9,149
  • 4
  • 32
  • 38
  • 2
    Couldn't you do your global exclude and then add the `log4j-over-slf4j.jar`? http://www.slf4j.org/legacy.html – Snekse Mar 26 '15 at 22:53
5

Even i encountered similar situation where i need to exclude spark jars from including in fat jar but test cases requires spark jars to execute.so below configuration worked for me. So basically i am adding compile time dependencies to test classpath. so for your problem below solution should work

configurations{
    runtime.exclude group: 'log4j'
}

test {
        classpath += configurations.compile
}
user3435860
  • 59
  • 1
  • 5
2

You should not need to define an exclusion. Unless you reconfigured something, a project's testRuntime configuration will only be used for that project's test task.

Peter Niederwieser
  • 121,412
  • 21
  • 324
  • 259
  • 1
    Sorry, I should have been more clear in my question about the reason for the exclusion. My project has a lot of dependencies on third-party libraries, and many of these libraries drag in unwanted transitive deps on log4j. I could add dep-level exclusions for each library, but of course it is a lot easier to block log4j at the config level. (I don't want to have the dreaded log4j appear again every time I add a new library!) – Chris B Mar 30 '12 at 00:38