How to make a subProjet add a dependency to the parent's buildscript?

I have a requirement to support different databases, however only one per deploy (every client wants to use a different database provider).
To handle the DB I am using JOOQ, which needs to generate the db classes for each DB type.
So in gradle I’ve defined a parent project that contains a task to call JOOQ’s generate, and I just want to define the database dependencies in the subProjects.
I thought I had done that, but as the task is defined in the parent project, when I run it in the sub-project the DB dependency is not on the buildscript classpath, throwing me a ClassNotFound error.
Is there a way to make the dependency of the sub-project be considered when running the task?

My attempt:
parent project’s build.gradle:
buildscript { dependencies { classpath 'org.jooq:jooq:3.8.+' classpath 'org.jooq:jooq-meta:3.8.+' classpath 'org.jooq:jooq-codegen:3.8.+' } } subprojects { task generateJOOQ { description 'Executes the JOOQ\'s Code Generator' ext.configFile = file('./jooq-generator.xml') ext.generatedDir = new File(projectDir, 'src/generated/java') inputs.file configFile outputs.dir generatedDir doLast { def jooqGenConfig = javax.xml.bind.JAXB.unmarshal(new FileReader(configFile), org.jooq.util.jaxb.Configuration.class) org.jooq.util.GenerationTool.generate(jooqGenConfig) } } }

In the subProject I’ve tried all the below, but none worked:
buildscript {
dependencies {
classpath ‘com.h2database:h2:1.4.+’
}
}

(this apparently has no effect at all)

and also

parent.buildscript {
	dependencies {
		classpath 'com.h2database:h2:1.4.+'
	}
}

this throws an error:
_Cannot change dependencies of configuration ":db:classpath" after it has been resolved._

and also

gradle.projectsLoaded {
	parent.buildscript {
		dependencies {
			classpath 'com.h2database:h2:1.4.+'
		}
	}
}

this also seems to have no effect

Any ideas?

You cannot modify the parent’s classpath from a child. That would be a chicken-and-egg problem. You can add the driver to the parent’s classpath.

Alternatively you can move the buildscript block into the subprojects:

subprojects {
  buildscript {...}
  task jooq {...}
}

Then adding drivers in the subprojects would work.

To be honest, neither of these solutions is nice, because they rely adding stuff to the buildscript classpath. A much nicer solution would be to use normal dependencies and then forking jooq. Warning, untested code ahead :wink:

configurations {
  jooq
}

dependencies {
  jooq 'org.jooq:jooq:3.8.+'
  ...
  jooq 'your-sql-driver'
}

task jooq(type: JavaExec) {
  def configFile = ...
  def outputDir = ...

  inputs.file configFile
  outputs.dir outputDir

  main = 'org.jooq.util.GenerationTool'
  classpath = configurations.jooq
  args configFile
}

@st_oehme it just caught my eye that you gave example of adding buildscript block under subprojects - is that a supported scenario? Is there any documentation about how the buildscript blocks are extracted?

So far I thought that the buildscript can only be a top-level closure in the build.gradle file of the top-level project (that is, after I was once burned by having Gradle silently ignore a buildscript block declared in a script plugin).

@ddimitrov The details are complicated and that’s one of the reasons we want to move away from the buildscript block and towards the plugins block and more declarativeness.

You cannot alter a project’s buildscript.classpath from a script plugin, because at the point where the script plugin is evaluated, the project’s buildscript.classpath is already resolved.

You can however alter a child’s buildscript.classpath from the parent, because the parent is evaluated first.

1 Like