Right way to generate sources in Gradle 7

I have found only quite old posts on the topic of generated sources. What is the right way to do it in Gradle 7? How do I tell it that some task generates the source code?

Usually I simply added dependencies for compileJava (and probably sourcesJar) tasks on the code-generating task. Is this the idiomatic way or there is something better?

Example:

plugins {
    id("java")
    id("maven-publish")
}

repositories {
    mavenCentral()
}

java {
    withSourcesJar()
}

tasks.register("copyTemplates", Copy) {
    from("templates")
    into("src/main/java")
}

tasks.named("compileJava").configure {
    dependsOn("copyTemplates")
}
tasks.named("sourcesJar").configure {
    dependsOn("copyTemplates")
}
1 Like

Don’t mix sources under version control (src/main/java) and generated sources in one folder. Even worse, do NOT put generated sources under version control - this is an anti-pattern and it makes everything just more complicated and fragile.

We place generated sources in a folder below the target build folder; e.g., build/generated/main/java. You will easily get used to that if you start accepting that they are not really sources: they are products (or artefacts) that have been generated dynamically during a build from real sources and are therefore not sources in the first place. Put in other words, they can be produced any time from the sources that are the basis for their generation (of course, provided you have a working build).

Considering generated Java “sources”, the folder in which you store them can then be easily added as another source folder:

sourceSets {
   main {
      java {
         srcDir "${buildDir}/generated/main/java"
      }
   }
}

As the build folder is usually ignored by version control, placing them below this folder also ensures that they are not inadvertently put under version control.

1 Like

Basically what @twwwt said, but more.

You should not only not mix generated and non-generated code.
You should also not share output folders among multiple tasks or up-to-date checks and caching might be disturbed.
So latest when you have mutliple code generation tasks, give each task a separate output folder, for example layout.buildDirectory.dir("generated/sources/$name/main/java").

Additionally it is preferrable to use implicit task dependencies over explicit task dependencies.
That way every task that needs sources automatically depends on the needed tasks and you don’t need manual task dependencies.
You can achieve that by using the task or task provider as srcDir directly.
It is almost always a sign of antipattern if you configure paths manually except for inputs / outputs of tasks.

So the full example would be:

plugins {
    id("java")
    id("maven-publish")
}

repositories {
    mavenCentral()
}

java {
    withSourcesJar()
}

def copyTemplates = tasks.register("copyTemplates", Copy) {
    from("templates")
    into(layout.buildDirectory.dir("generated/sources/$name/main/java"))
}

sourceSets {
    main {
        java {
            srcDir(copyTemplates)
        }
    }
}
1 Like