Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capability conflicts not always detected (when coming from transitive dependencies) #12011

Closed
tbroyer opened this issue Jan 26, 2020 · 5 comments

Comments

@tbroyer
Copy link
Contributor

tbroyer commented Jan 26, 2020

Expected Behavior

A capability conflict is detected.

Current Behavior

Both dependencies appear in the final classpath (and deliverable, as we're packaging them into an RPM)

Context

I'm using @ljacomet's dev.jacomet.logging-capabilities plugin to detect (and resolve) conflicts between logging frameworks. My actual project is more complex than the reprocase below with a dozen subprojects and a couple platforms, and many logging frameworks coming transitively from external dependencies. I was using "custom" capabilities and things worked correctly, it only fails (i.e. build doesn't fail, but fails to detect conflict) with the dev.jacomet.logging-capabilities plugin, with the org.slf4j:slf4j-log4j12 (transitive) dependency.
In my case, there's no conflict detected between it and org.apache.logging.log4j:log4j-slf4j-impl, or even its transitive log4j:log4j and org.apache.logging.log4j:log4j-1.2-api.

With custom capabilities
plugins {
    java
    // id("dev.jacomet.logging-capabilities") version "0.7.0"
}
repositories {
    mavenCentral()
}
dependencies {
    implementation("org.apache.logging.log4j:log4j-1.2-api:2.13.0")
    implementation("org.slf4j:slf4j-log4j12:1.7.30")
}

// cf. http://www.slf4j.org/manual.html#swapping
val slf4jProviders = setOf(
    "org.slf4j:slf4j-log4j12",
    "org.slf4j:slf4j-jdk14",
    "org.slf4j:slf4j-nop",
    "org.slf4j:slf4j-simple",
    "org.slf4j:slf4j-jcl",
    "ch.qos.logback:logback-classic",
    "org.apache.logging.log4j:log4j-slf4j-impl",
    "org.apache.logging.log4j:log4j-slf4j18-impl"
)
// cf. http://www.slf4j.org/legacy.html
val slf4jBridges = mapOf(
    "org.slf4j:jcl-over-slf4j" to "commons-logging:commons-logging-api",
    "org.slf4j:log4j-over-slf4j" to "log4j:log4j",
    "org.apache.logging.log4j:log4j-to-slf4j" to "org.apache.logging.log4j:log4j-core"
)
// cf. https://logging.apache.org/log4j/2.x/maven-artifacts.html#Optional_Components
val log4jProviders = setOf(
    "org.apache.logging.log4j:log4j-to-slf4j",
    "org.apache.logging.log4j:log4j-core"
)
val log4jBridges = mapOf(
    "org.apache.logging.log4j:log4j-1.2-api" to "log4j:log4j"
)
val julBridges = setOf(
    "org.slf4j:jul-over-slf4j",
    "org.apache.logging.log4j:log4j-jul"
)
dependencies {
    val conflicts = mapOf(
        "commons-logging:commons-logging" to "commons-logging:commons-logging-api"
    ) +
        slf4jProviders.associateWith { "org.slf4j:slf4j-provider-capability" } +
        slf4jBridges +
        log4jProviders.associateWith { "org.apache.logging.log4j:log4j-provider-capability" } +
        log4jBridges +
        julBridges.associateWith { "reprocase:jul-bridge-capability" } +
        (slf4jBridges.values + log4jBridges.values).associateWith { "reprocase:logging-capability" }
    conflicts.forEach { module, capability ->
        components.withModule(module) {
            allVariants {
                withCapabilities {
                    val (group, name) = capability.split(':')
                    addCapability(group, name, id.version)
                }
            }
        }
    }
}

Steps to Reproduce

This is an issue similar to #11300; reproducible with a build script is as simple as:

plugins {
    java
    id("dev.jacomet.logging-capabilities") version "0.7.0"
}
repositories {
    mavenCentral()
}
dependencies {
    implementation("org.apache.logging.log4j:log4j-1.2-api:2.13.0")
    implementation("org.slf4j:slf4j-log4j12:1.7.30")
}

or

plugins {
    java
    id("dev.jacomet.logging-capabilities") version "0.7.0"
}
repositories {
    mavenCentral()
}
dependencies {
    implementation("org.apache.solr:solr-core:8.4.1") {
        exclude(group = "org.restlet.jee")
    }
    implementation("eu.ralph-schuster:csv:2.8.1")
}

and gradle dependencies --configuration=runtimeClasspath.

In the build scans (see below), we can see that log4j:log4j and org.apache.logging.log4j:log4j-1.2-api both have the dev.jacomet.logging:slf4j-vs-log4j2-log4j:1.0 capability (listed twice btw) but they're marked as (not requested).

Your Environment

Build scan URL: https://scans.gradle.com/s/zipvgdx7l2256, https://scans.gradle.com/s/afx7f6muj4zwa
With custom capabilities: https://scans.gradle.com/s/yzuoorebczaak, https://scans.gradle.com/s/i2l76pvt72ehe

@tbroyer tbroyer changed the title Capability conflicts not always detected Capability conflicts not always detected (when coming from transitive dependencies) Jan 26, 2020
@ljacomet ljacomet self-assigned this Jan 27, 2020
@ljacomet
Copy link
Member

Thanks for the report.
Unfortunately, I cannot reproduce it.

Here is a build scan for the second sample showing errors as expected. I also tried Gradle 4.10.3, 5.6.4 and 6.0.1 which all fail as expected.

And here is the buildscript used:

/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java Library project to get you started.
 * For more details take a look at the Java Libraries chapter in the Gradle
 * User Manual available at https://docs.gradle.org/5.2.1/userguide/java_library_plugin.html
 */

plugins {
    // Apply the java-library plugin to add support for Java Library
    `java`

    id("dev.jacomet.logging-capabilities") version "0.7.0"
}

repositories {
    // Use jcenter for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    mavenCentral()
}

dependencies {
//    implementation("org.apache.logging.log4j:log4j-1.2-api:2.13.0")
//    implementation("org.slf4j:slf4j-log4j12:1.7.30")
    implementation("org.apache.solr:solr-core:8.4.1") {
        exclude(group = "org.restlet.jee")
    }
    implementation("eu.ralph-schuster:csv:2.8.1")

}

Anything you could have like an init script or local tweaks that could explain the difference?

@tbroyer
Copy link
Contributor Author

tbroyer commented Jan 27, 2020

Well, I can reproduce within a Docker image and the attached project, so it must be on your side 😉 :

  1. unzip attached project then cd to the directory you extracted it
  2. run ./reprocase.sh; this is equivalent to:
docker run -it --rm -v $(pwd):/reprocase -u 1000:1000 -w /reprocase gradle:6.0.1-jdk8 gradle dependencies --configuration=runtimeClasspath

@ljacomet
Copy link
Member

So that is really interesting.

  • If I run your sample locally, in docker, it indeed fails to detect the conflicts.
  • If I run your sample without docker, specifying a new gradle home (-g home611), it fails to detect the conflicts.
  • If I run my sample, specifying a new gradle home (-g home611), it detects the conflicts.
  • If I wipe both .gradle and home611 in the two project folders, and run them again, I get the same results. One fails to detect, the other does detect.

And there is no meaningful difference in the build files from what I can see. I have no Gradle init script available, use the wrapper in both cases. --version reports the same information. I even tested after killing all daemons and running with --no-daemon.

I'll keep digging to find something but I admit this is really puzzling.

@ljacomet
Copy link
Member

And indeed, the difference was on my side. Forgot to double check the settings.gradle.kts 😲 🤦‍♂

I can reproduce the issue now and will investigate if this is a Gradle issue or a plugin one.

@ljacomet
Copy link
Member

So the issue has been identified and relates to an interaction between capabilities and virtual platforms for alignment.

A fix is running through CI and will (most likely) be available in Gradle 6.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment