Hi Christos,

CCing dev@solr so that it stays in the archives.

I wanted to reach out to you to ask how you would resolve dependency
> conflicts with the carrotsearch dependency checks plugin. I am using the
> same dependencies.gradle file as in the Lucene project in Solr and I have
> found a way to "resolve" them, but I am not sure if that's the intended way.
>

Ok, so perhaps I should clarify that one of the objectives of removing the
consistent-versions plugin was to rely on gradle's built-in mechanisms. The
plugin you can see in Lucene (com.carrotsearch.gradle.dependencychecks) is
passive and only there to signal problems with conflicting versions in a
set of configurations we want to be "consistent". Any conflict dependency
or alignment should be done using gradle's mechanisms. I should also
mention that gradle's "conflicts" are a bit different to what I consider a
"conflict". For example, consider two subprojects A and B, where A depends
on library foo, B depends on library foo and baz:

A: foo
B: foo, baz

If you declare a version of foo in your catalog (say, 1.0) and baz has a
transitive dependency on foo 1.1, gradle will automatically bump foo to 1.1
in project B. So A will have foo 1.0 and B will have foo 1.1. This is how
gradle handles version resolution by default. There are various ways to
"align" those dependencies but I find all of them confusing (platforms,
etc.), sorry. Maybe you'll have more luck in applying those. :)

So, back to the main topic, the reason that custom plugin of mine is in
Lucene, is to detect situations when the same artifact exists in different
versions in a set of configurations of different subprojects. When such a
situation happens, it'll abort the build but won't do anything
automatically. How to resolve it is up to you. I opted to cross-check these:

  def mainConfigurations = project.configurations.matching {
    it.name in [
      "compileClasspath",
      "runtimeClasspath"
    ]
  }

  def testConfigurations = project.configurations.matching {
    it.name in [
      "annotationProcessor",
      "testCompileClasspath",
      "testRuntimeClasspath"
    ]
  }

1. Add the conflicting dependency to the versions catalog (if not present)
> with the highest version from the conflict report
>

This is one possibility, yes. I always found it problematic to keep such
manually-tuned rules consistent in the presence of independent library
upgrades.


> 2. Force the dependency to the configuration group in
> the dependencies.gradle file via
> allprojects.dependencies.constraints.[configurationSet].configureEach {
> Configuration conf ->
>   conf.resolutionStrategy {
>     force [conflicting dependency from catalog]
>   }
> }
>

I think this one is better. You can also add constraints like so
('consolidatedConfigurations' is a set of configurations we want to apply
the constraints to - it's typically the same :

def consolidatedConfigurations = ["compileClasspath",  "runtimeClasspath",
(...and more) ]

  dependencies {
    constraints {
      consolidatedConfigurations.configureEach { Configuration conf ->
        add(conf.name,  deps.log4j, {
          because("Use consistent log4j version.")
        })
        add(conf.name, deps.junit, {
          because("Use consistent JUnit version.")
        })
      }
    }
  }

these constraints are hints to gradle's version resolution [1]. It's still
a pain to figure out when such a constraint becomes unnecessary (for
example, because a dependency upgraded its transitive dependency or
something else has changed). I haven't found a way to detect unused
constraints automatically.


> I would go through the carrotsearch plugin and read the docs, but I
> couldn't find any. Do you maybe have a guide or something that I could use
> for reference?
>

No, sorry for this. The source code is your guide. :) It's really a simple
collection of dependencies from multiple configurations, then detecting
whether the same artifact has multiple versions and failing the build if
this happens. If all dependencies are consistent, they're checked against
versions.lock - if there are any differences, it means something has
changed (a direct or transitive dependency version has changed or has been
removed/ added). Such changes are reported and fail the build but this is
done for informational/ sanity reasons. I just don't trust gradle's
resolution mechanisms and want to make sure it works the way I want it to
work... :)

You can remove this plugin entirely if you want to - everything else should
work the same. I think it's good to have a plain-text list of all
dependencies in configurations we care about though -

  "configurationGroups" : {
    "main_dependencies" : {
      "com.carrotsearch.randomizedtesting:randomizedtesting-runner:2.8.1" :
"fa9ef26b,refs=4",
      "com.ibm.icu:icu4j:74.2" : "47ea4550,refs=6",
      "commons-codec:commons-codec:1.13" : "e9962aab,refs=4",
      "io.sgr:s2-geometry-library-java:1.0.0" : "cbc357ab,refs=4",
...

I would also recommend that you take a look at the Lucene commits that were
part of the patch that removed palantir's consistent versions plugin -
there were some quirks and rough edges that you'll see in the diffs. I'm
sure it'll be a similar situation in Solr.

If you can't figure out something, send an email to dev and CC me. I am by
no means a gradle expert but maybe I've come across something that I'll be
able to explain.

Good luck!
Dawid

[1]
https://docs.gradle.org/current/userguide/dependency_constraints.html#sec:adding-constraints-transitive-deps


>

> Sincerely,
> Christos Malliaridis
>

Reply via email to