Multi-project generate and compile source

(Raleigh) #1

Hi All,

I have a multi-project build in which I am trying to generate some (java) source from a template file in a subproject.
However my genSrc task never executes, even if I invoke it directly:

λ gradle :tools:subProject:genSrc
:tools:subProject:genSrc UP-TO-DATE

I’ve tried to follow examples found in the forums but there doesn’t seem to be a consensus on the correct approach.

rootProject build.gradle


subprojects {
    apply plugin: 'java'

    buildDir = 'out'
    task genSrc(type: Copy) << { task ->
        println "Processing templates from: $rootProject.sourceSets.templates"

        from rootProject.sourceSets.templates
        into task.project.buildDir + "/gen/src/main/java/" + task.project.props['packageName'].replace('.', '/')

        eachFile { fileCopyDetails ->
            rename {
      '.template', '')
            println "transform file " +

sourceSets {
    templates {
        java {
            srcDirs = ['src/templates']

subproject build.gradle

project.ext {
    props = ['packageName': "com.acompany.apackage"]
compileJava {
    dependsOn genSrc
    source 'out/gen/src/main/java/'

Directory structure:

    |    \---templates

(Janito Vaqueiro Ferreira Filho) #2

Your copy task never executes because by the time it executes, it has no inputs configured, so Gradle thinks it’s up to date. You can remove the << after the task declaration, and the closure will be executed immediately in order to configure the task. Afterwards, when configuration of the project ends, the task is executed (if requested or is a dependency of a requested task).

The << operator calls the Task.leftShift() method, which appends the closure to the task action list. Therefore, the closure after the << contains the code to be executed when the task is executed, which in this case configures the task, but because the task wasn’t configured previously, it’s never executed.

Hope this helps :slight_smile:

(Raleigh) #3

Thanks, but it seems that your suggestion doesn’t work as expected.

There is a bit of a chicken&egg problem resolving the source.

If I remove the << operator the build fails because (I’m guessing) the SourceSets haven’t been configured yet.

* What went wrong:
A problem occurred evaluating root project 'stest'.
> Could not get unknown property 'templates' for SourceSet container.

Seems like there needs to a dependency in this task but everything I’ve tried doesn’t work either.

(Janito Vaqueiro Ferreira Filho) #4

Just to be sure, are you certain all sub-projects have a templates source set?

(Raleigh) #5

For this particular case there is one template file that all the projects should use. Therefore, just the root project has a templates source set. Also, not all projects need/use this task.

If I move the SourceSets to the subprojects block then I run into other issues (such as property not defined) so it seems that having this task defined there is not the right approach.

I suppose it would be better to put this task in a separate build script/plugin and only include it in the subprojects that require it. I’m not exactly sure how to go about doing that but if I recall correctly it is possible.

(Raleigh) #6

I was able to solve my issue by putting the task in a seperate .gradle file and using
apply from: "$rootProject.projectDir/genSrc.gradle"
in the subprojects I need

Here is the build file with the task to generate the required files

apply plugin: 'de.fuerstenau.buildconfig'

project.ext {
    props = ['packageName': project.packageName]

buildConfig {
    appName =
    clsName = 'VersionInfo'      // sets the name of the config class
    packageName = 'com.xzy.util'// sets the package of the config class,
    charset = 'UTF-8'            // sets charset of the generated class,
    buildConfigField 'String', 'TARGET_JRE', String.valueOf(project.targetJRE)
    buildConfigField 'String', 'BUILD_JRE', String.valueOf(JavaVersion.current())
    buildConfigField 'String', 'BUILD_DATE', project.buildDate

task genSrc(type: Copy)  { task ->

    def toDir = "$task.project.buildDir/gen/src/main/java/${task.project.props['packageName'].replace('.', '/')}"

    from rootProject.sourceSets.templates.allSource
    into toDir
    rename('.template', '')
    eachFile { file ->
        println "transform file " +

compileJava {
    dependsOn genSrc
    source 'out/gen/src/main/java/'