While providing a solution customized for many customers, you might want to package an application artefact in multiple flavours. Each with a set of dependencies to provide just the customer specific implementations.

With gradle this can be accomplished fairly easy. To demonstrate it, I’ve created an example project available on github.

It consists of five modules:

  • an application module
  • an API interface module
  • three modules as API implementations

The API interface module is not needed during runtime but helpful during development time. It helps to enforce the same expected interface in the implementation classes.

The lib-impl* modules contain each a custom implementation for the CustomDataProvider and demonstrate the different flavours of the artefacts.

Now, the question is how to provide several artefacts of the application, each bundled with one of the library implementations.

One solution (as always, there a probably many possible out there) is to use Gradle configurations with the Shadow Plugin. Using the shadow plugin is the exchangeable part here. As we basically do rely on the grade configurations, the way the application is package is completely open and this solution can probably also be applied to other packaging plugins.

Having a look at the build.gradle of the application module reveals that we create a configuration for each flavour we want to have a seperate artefact for.

configurations {
    customerAcompile
    customerBcompile
    customerCcompile
}

Now, we add the dependencies…

dependencies {
    customerAcompile project(':lib-implA')
    customerBcompile project(':lib-implB')
    customerCcompile project(':lib-implC')
}

To make it easier to execute sub-steps of the build, it’s nice to have a task for each artefact to be generated. Thus we need a way to dynamically add build tasks for each variant we want to build. In our case the type of such is a ShadowJar task. To make this concise, clean and easy to extend, we can use the Gradle DSL based on Groovy.

First, we define a array of the variants we want:

ext {
    variants = [
            [name: "customerA"],
            [name: "customerB"],
            [name: "customerC"]
    ]
}

And then we use that array to loop over and create the build tasks:

variants.each { variant ->
    task "${variant.name}ShadowJar"(type: ShadowJar) {
        classifier = "${variant.name}"
        from sourceSets.main.output
        configurations = [project.configurations."${variant.name}compile"]
        manifest {
            attributes 'Main-Class': mainClassName
        }
    }
    shadowJar.dependsOn("${variant.name}ShadowJar")
}

The classifierproperty is used to get an distingushable artefact name. from and configurations make up the thing comined together into the shadowJar artefact.

If you need to apply the base idea of this article to another packaging context, those two properties are probably those you want to take and apply accordingly.

To get executeable jar artefacts, we define the Main-Class name as an attribute for the manifest. ShadowJar usually adopts it automatically when the application plugin is used, but as we are using a custom shadowJar task here, we also need to apply it with those three lines.

Finally, by defining the dependency with dependsOn, we can afterwards just type gradle shadowJar to have all variants build and waiting for us in build/libs/. Execution shows the expected behaviour for each of them:

➜ java -jar build/libs/application-customerC.jar                               
s = Implementation or customer C

➜ java -jar build/libs/application-customerB.jar
s = Implementation or customer B

➜ java -jar build/libs/application-customerA.jar
s = Implementation or customer A

Have fun building the solutions that make your customers happy! 😃