Conditionally include/exclude Java source sets

Hi All,
I am in the process of converting an existing java build to gradle and have a question on best practices.

Suppose I have a source folder with files
a.java, b.java, c.java, d.java, e.java, f6.java, f7.java, gX.java, gY.java

What is the proper and canonical way to include/exclude files based on a build parameter?
For example, if targetCompatibility is 1.6 include f6.java otherwise include f7.java.
Or if project property == some_value include gX.java else include gY.java?

Ideally I would prefer not to move the source(s) to a different folder, however that isn’t a strict requirement

You could exclude the required files using something like

sourceSets {
    main {
        java {
            if( targetcompatibility == JavaVersion.VERSION_1_7 )
                exclude '**/f6.java'
            else
                exclude '**/f7.java'
        }
    }
}
1 Like

Another option is to define a separate sourceSet for each variant and (maybe, for clarity) using separate folders for each variant. Then you could create a jar task and test task for each variant. The benefit is that you would no longer have a magic system property, so your build becomes easier to understand for others. You could even build all variants in one Gradle invocation.

1 Like

Thanks,
That is what I am doing now. I am just wondering if this is the “correct” way.

That sounds like an interesting solution.
Is there an example of a project that does this? My project is a multi-project build with many subprojects.

How would one go about building all variants in one Gradle invocation?

I don’t have an example project, but here’s some snippets to get you started. You can look into the the Api for SourceSet and the Jar and Test task for more details. The idea is that all variants share most of the code in src/main/java, but that that folder is never built by itself, but always together with variant specific sources.

apply plugin: 'java-base'
sourceSets {
  variantA {
    java.srcDirs 'src/main/java', 'src/variantA/java'
  }
  variantB {
    java.srcDirs 'src/main/java', 'src/variantB/java'
  }
}

variantAJar(type: Jar) {
  from sourceSets.variantA.output
}

variantBJar(type: Jar) {
  from sourceSets.variantB.output
}

Now when you call gradle assemble, you’ll get both jars without having to pass any variable to the build. Users can easily discover how your build works by calling gradle tasks.

You could then get rid of the repetition by building a little ‘variants’ plugin. Then your actual builds could look as simple as

apply plugin: 'java-variants'

variants {
  variantA {
    // potentially some variant config here
  }
  variantB {}
}

I think the bigger challenge would come with depending on other projects. The java6 variant of project X needs to compile against the java6 variant of project Y. It’s definitely doable (The Android Gradle plugin does this for instance), but I admit that it is quite a bit more effort than the property based solution. So the pay-off really depends on how often you will do these kinds of complex projects and how many other people need to work with it every day.

1 Like