Using a Custom SourceSet in Gradle

Viewed 892

According to this from the official docs and this SO answer, defining a new source set should be as simple as (in kotlin):

sourceSets {
    create("demo") {
        compileClasspath += sourceSets.main.get().output
    }
}

The second reference also explicitly claims this can now be used in building a jar. However, while the above does not throw an error, actually trying to use the new source set anywhere does. Minimal example (generated with gradle init -> "kotlin application" and project name "foobar"):

plugins {
    id("org.jetbrains.kotlin.jvm").version("1.3.21")
    application
}

repositories {
    jcenter()
}

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
}

application {
    mainClassName = "foobar.AppKt"
}

sourceSets {
    create("demo") {
        compileClasspath += sourceSets.main.get().output
    }
}

I've removed configuration of the test source set since it isn't relevant here. The src directory looks like this (which is a bit non standard, but I think fine for the discussion):

src
├── demo
│   └── Demo.kt
└── main
    └── foobar
        └── App.kt

Ideally, I'd like to have completely separate source trees (but this question is not specifically about that). What this builds makes sense in relation to the configuration above, but it is not really the goal. To do that, I want to add a custom jar or two.1

Unfortunately though, the demo source set can't be referenced in the gradle script.

val jar by tasks.getting(Jar::class) {
    manifest { attributes["Main-Class"] = "DemoKt" }

    // HERE IS THE PROBLEM:
    from(sourceSets["demo"].get().output)
    dependsOn(configurations.runtimeClasspath)
    from({
        configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) }
    })
}

This fat jar task definition is taken from the gradle docs and I've used much before.
As is, gradle chokes:

Script compilation errors:


  Line 28:      from(sourceSets["demo"].get().output)
                                    ^ Unresolved reference.

If I change that line to:

from(sourceSets.demo.get().output)

Which is the syntax used with sourceSets.main, then the error becomes:

 Line 28:      from(sourceSets.demo.get().output)
                            ^ Unresolved reference: demo

How am I supposed to work with the custom source set?


  1. I am aware of the gradle subproject pattern and have used it to do much the same thing, but I'd prefer the simple separate source sets solution, which does I think meet all three criteria for such mentioned in that doc.
1 Answers

The sourceSets property (an extension property for Project) returns a SourceSetContainer which extends NamedDomainObjectContainer<SourceSet>. The latter interface defines get(String) (an operator extension function) which is what allows you to use the ["..."] syntax. That method returns T directly, meaning you should just be using:

from(sourceSets["demo"].output)

The reason you have to use get() when using sourceSets.main is because you're getting a NamedDomainObjectProvider<SourceSet> which wraps the source set.

You can also get the custom source set via delegation

  1. When creating it:

    val demo by sourceSets.creating { /* configure */ }
    
  2. Or some time after creating it:

    val demo by sourceSets
    
Related