How to substitute buildscript dependency?

Hi,

I’m working on a plugin for Gradle. The project has both the plugin’s code and code for an example that uses the plugin. When developing, I’d like the example project to use plugin’s code from the same project / Git repository. However, I cannot get dependency substitution to work in this case.

For the example project I have buildAgainstPluginProject=true in gradle.properties and the following in build.gradle:

buildscript {
    ext.jobDslPluginVersion = '2.0.0'

    repositories {
        mavenLocal()
        jcenter()
        maven {
            url 'http://repo.jenkins-ci.org/releases/'
        }
    }

    dependencies {
        classpath "com.here.gradle.plugins:gradle-job-dsl-plugin:${jobDslPluginVersion}"
    }

    if (project.properties.buildAgainstPluginProject.toBoolean()) {
        configurations.all {
            resolutionStrategy.dependencySubstitution {
                substitute module('com.here.gradle.plugins:gradle-job-dsl-plugin') with project(':plugin')
            }
        }
    }
}

apply plugin: 'com.here.jobdsl'

repositories {
    mavenLocal()
    jcenter()
    maven {
        url 'http://repo.jenkins-ci.org/releases/'
    }
}

dependencies {
    compile localGroovy()
    compile "com.here.gradle.plugins:gradle-job-dsl-plugin:${jobDslPluginVersion}"
}

if (properties.buildAgainstPluginProject.toBoolean()) {
    configurations.all {
        resolutionStrategy.dependencySubstitution {
            substitute module('com.here.gradle.plugins:gradle-job-dsl-plugin') with project(':plugin')
        }
    }
}

But with this I’m getting:

* What went wrong:
A problem occurred configuring project ':example'.
> Could not resolve all files for configuration ':example:classpath'.
   > Could not resolve com.here.gradle.plugins:gradle-job-dsl-plugin:2.0.0.
     Required by:
         project :example
      > Project :example declares a dependency from configuration 'classpath' to configuration 'default' which is not declared in the descriptor for project :plugin.

I wonder whether is has anything to do with the specialty of this project that gradle-job-dsl-plugin is both a buildscript-classpath-dependency and a project-compile-dependency.

The project’s full source code is at https://github.com/heremaps/gradle-job-dsl-plugin/tree/dep-subst (in the dep-subst branch).

Any hints how to make this work?

1 Like

Hey Sebastian,

dependency substitution does not work here as you try to replace a dependency in buildSrcript block with a project that is part of the multiproject build.

The buildscript block has has no access to projects in a multiproject build. I would suggest to use composite builds here in the following way:

  • have two (separate) builds in your repository (plugin + example)
  • in your settings.gradle for the example build you can have

——
if(System.getProperty(‘buildAgainstPluginProject’) {
includeBuild ‘…/plugin’
}
——

Now if you run your example project with -DbuildAgainstPluginProject=true the binary dependency to your plugin (from the example builds buildScript block) will be replaced with a local project dependency.

hope that helps!

cheers,
René

Hi René, hope you’re doing good! :slight_smile: I’m a bit confused about you mentioning buildSrc here as there is no buildSrc directory in this project. Do you mean the code from the buildscript closure, and is that treated the same as code in a buildSrc directory?

Doing great thanks!

Sorry my fault. mixed things up here. but basically what I said about buildSrc is true for buildscript too: no access to projects.

buildscript is evaluated early and all dependencies here must be resolved the actual build can happen. therefore there is some sort of chicken egg problem. just go with composite builds :wink:

I’m trying, but I find the docs a bit lacking. E.g. they say “Every included build must have a settings.gradle file”, but not what for. What am I supposed to define in an included build’s settings.gradle file?

tbh. I’m not exactly sure why a settings.gradle is required here but you can just put in the name of your root project:

//settings.gradle
rootProject.name = “example”

as a side note I think it’s a general good pattern to have a settings file that defines the root project name. this way your build is not tight to the directory name which might cause issues when checking out to different dir names on CI / dev boxes.

One more follow-up question: What’s the recommended practice regarding the Gradle wrapper in composite builds? Usually each separate project that’s part of the composite build defines its own wrapper. Which Gradle executable should be used to build the composite build then? It sort of feel silly to add yet another wrapper to the composite build itself, yet calling an arbitrary wrapper from the included builds does so, too.

FYI I use a version.txt file in the root directory to keep the plugin version in sync across the two builds without needing to update in two places.

Eg:

version = file('../version.txt').text.trim()

See here and here

So I finally found the time to try this, and it does not work. The plugin project simply does not get built. I also tried on the command line with

cd example
gradle --include-build ../plugin assemble

and it did not work.