A comment on defaultDependences

Gradle 2.5 added defaultDependencies for configuration. However looking at the usage of the code compared so some prior methods, hardly provides any better readability within plugin code.

Here is a stripped-down example from the Asciidoctor project in the pre-2.5 way

void apply(Project project) {
    project.extensions.create('asciidoctorj',AsciidoctorJExtension,project)
    project.configurations.maybeCreate( 'internal_asciidoctorj' ) 

    project.afterEvaluate {
        project.dependencies {
            internal_asciidoctorj "org.asciidoctor:asciidoctorj:${project.asciidoctorj.version}" 
        }
    }
}

THe above is a more common use case of default dependencies, that I think the Gradle docs aludes to. A script user can override the version of asciidoctorj but setting it within an extension block. For this to be evaluated correctly, addition of the dependency has to happen as late as possible, hence the use of afterEvaluate above.

Now compare this with 2.5+ way

void apply(Project project) {
    project.with {
        def asciidoctorj = extensions.create('asciidoctorj',AsciidoctorJExtension,project)
        def conf = configurations.maybeCreate( 'internal_asciidoctorj' ) 

        project.afterEvaluate {
          conf.defaultDependencies { dependencies ->
              dependencies.add( dependencies.create("org.asciidoctor:asciidoctorj:${asciidoctorj.version}") ) 
          }
        }
    }
}

Unless I’m missing something, I can hardly see an improvement here. The code is hardly more readable or shorter (except for afterEvaluate no longer required.

That’s not quite the same thing. In the first case, someone could do:

dependencies {
    internal_asciidoctorj 'my.company:asciidoctorj-fork:1.0'
}

And you’d add the “regular” org.asciidoctor:asciidoctorj dependency anyways.

With defaultDependencies, that doesn’t happen because the regular dependency isn’t added because the configuration isn’t empty. So defaultDependencies is to allow the user to specify something completely different, if they know better, or otherwise use a sane default.

@sterling that is technically true. The use of the covert configurations tends to rely on the fact that the user would not add the dependendency themselves, but rather use the more descriptive value set through an extension.

To bring the two closer to wach other, one would have to add a conditional clause in the afterEvaluate closure to check whether the dependency has not already added before attempting to add it. THat would add a number of line to the original code snippet, which would make the defaultDependencies version look shorter.

Thanks for your answer, it highlighted the fact of only adding the dependency if it has not been added before.

P.S. It would be nice if there was a another version of the defaultDependencies method which only took a map or a string, so as to shorten usage for the common case. i.e.

  conf.defaultDependency "org.asciidoctor:asciidoctorj:${asciidoctorj.version}" 

Actually reviewing this, I have one more question:

Firstly a contrived build.gradle

configurations {
  test
  test2
}

repositories {
  jcenter()
}

dependencies {
  test 'org.asciidoctor:asciidoctorj:1.5.2'
  test 'org.asciidoctor:asciidoctorj:1.5.1'
  test2 'org.asciidoctor:asciidoctorj:1.5.1'
  test2 'org.asciidoctor:asciidoctorj:1.5.2'
}

Now running gradle dependencies (testes with both 2.0 & 2.7), one gets

:dependencies

------------------------------------------------------------
Root project
------------------------------------------------------------

test
+--- org.asciidoctor:asciidoctorj:1.5.2
|    +--- org.jruby:jruby-complete:1.7.16.1
|    +--- org.slf4j:slf4j-api:1.7.7
|    \--- com.beust:jcommander:1.35
\--- org.asciidoctor:asciidoctorj:1.5.1 -> 1.5.2 (*)

test2
+--- org.asciidoctor:asciidoctorj:1.5.1 -> 1.5.2
|    +--- org.jruby:jruby-complete:1.7.16.1
|    +--- org.slf4j:slf4j-api:1.7.7
|    \--- com.beust:jcommander:1.35
\--- org.asciidoctor:asciidoctorj:1.5.2 (*)

(*) - dependencies omitted (listed previously)

It is clear, as I would have expected, that as their are no special configuration rules, 1.5.2 will always be the winner, irrespective of the order they werre added to the configuration.

If I go back to the original afterEvaluate example andhave something that did

    project.afterEvaluate {
        project.dependencies {
            internal_asciidoctorj "org.asciidoctor:asciidoctorj:1.5.2" 
        }
    }

but the script author wrote

dependencies {
  internal_asciidoctorj "org.asciidoctor:asciidoctorj:1.5.1" 
}

the eventual outcome would be that 1.5.2 would be the selected dependency. However should I now use

def conf = project.configurations.internal_asciidoctorj
conf.defaultDependencies { deps ->
  deps.add( project.dependencies.create(
      "org.asciidoctor:asciidoctorj:1.5.2"
   )) 
 }

and the script author opts for 1.5.1 again, then as I understand it the expected behvaiour is that 1.5.1 will win.

Correct?