19
votes

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.
3

3 Answers

9
votes

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.

5
votes

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

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.