Multiproject producing multiple artefacts depending on customer specific configurations


(Carlo Luib-Finetti) #1

I wonder what is the best way to organize Gradle builds, given that

  • we have a multiproject with sub projects, resulting into simple jars (ok, this is simple, and I got it),

  • we have to produce different end artefacts for deployment (Zip, WAR, Tar, etc) for different runtime nodes (Rich Client, Web Servers, standalone Java Servers, standalone console clients), assembling jars and configuration files,

  • where each build is a customer specific build, i.e.: 80-90% of the produced jars are customer agnostic jars, but the rest are customer specific jars, and the end artefact have customer specific configuration files and use different 3rd party libraries.

Is Gradle’s “configuration” concept the way to organize such a build structure?

How do I tell Gradle which customer specific build it should process?

How do I define a WAR / a ZIP / a TAR configuration which respects these customer specific definitions?

Please, can you suggest some outline?


(Hans Dockter) #2

I don’t think you would need to introduce configurations to solve this.

In the root build script I would create a couple of Zip/War/Jar tasks that are representing the uberarchives. In the configuration of those uberarchives you can iterate over the subprojects to add the subproject jars. If things are getting to lengthy you might want to modularize and create a script for each uberarchive.

Does customer specific jars mean customer specific code? Does each customer specific jar have its own subproject? Or is it rather a parameter on an existing subproject that somehow changes depending on for which customer you build a distribution?


(Carlo Luib-Finetti) #3

thanks for your reply

“customer specific jar” means a jar built from a distinct subproject assigned to a customer. So I have the set of jars (subprojects) common to all our customers, and another set of jars which should be built and deployed when I say “do a build for customer X”.

These customer jars add to the end artefacts (WAR, ZIP et al). So maybe it is a good idea to define the set of common jars and the set of customer specifc jars for each customer, and add them to customer specific definitions of the end artefacts.


(Carlo Luib-Finetti) #4

To solve my requirements, I tried with this approach. In my root build.gradle, I definied customer related tasks and an external property like this:

task BGETEM_build (type: GradleBuild) {
 buildFile = 'build.gradle'
 ext.customer = 'BGETEM'
 tasks = ['assemble']
}
    task UKPT_build (type: GradleBuild) {
 buildFile = 'build.gradle'
 ext.customer = 'UKPT'
 tasks = ['assemble']
}

Then, in the build file where the war is built, I tried with the following on dependencies declaration and (alternatively) in the war configuration:

dependencies {
  runtime project(':de.uvdms.util')
        if (customer == 'UKPT') {
  runtime project(':de.uvdms.ukpt.common')
  runtime project(':de.uvdms.ukpt.server')
  runtime project(':de.uvdms.ukpt.util')
 }
}
  war
{
     if (customer == 'UKPT') {
 into ('WEB-INF/lib')
 from project(':de.uvdms.ukpt.common').sourceSets.main.output.libdir
 from project(':de.uvdms.ukpt.server').sourceSets.main.output.libdir
 from project(':de.uvdms.ukpt.util').sourceSets.main.output.libdir
   }
}

Well, Gradle does the build without complaints, produces the war, but the desired jars from above are missing in the war.

What is going wrong here? Is it a good idea to have conditional dependencies or is it better to have the condition in the war configuration?


(Carlo Luib-Finetti) #5

Some questions to the above samples:

  • is it possible at all to have conditionalized dependencies constructed like the sample above? - if it is (what i suppose): what might be the pitfalls if it is not working?

  • if condition expressions in the war closure were ok: then I would have to state the implicit dependencies there elsewhere as explicit dependencies?

  • what is meant by “ueberarchives” in Hans’ answer?


(René Groeschke) #6

hey carlo, I’m not sure that we are on the right page here.

in your original post you wrote you have one multiproject build. now this looks like you have seperate gradle builds and try to compose them.

In general you can have such conditionalized dependencies. The problem is that you trigger this gradle build from another build script using the GradleBuild task (right?). The customer property you define in the GradleBuild task is added to the GradleBuild task instance itself and not passed to the build you want to execute. What might do the trick is to pass the customer property via -P commandline property to the underlaying gradle build.

Though I havn’t look on your build environment, it seems that having a “real” gradle multiproject build is would be much easier to maintain and instead of resuing the same tasks with different flags,

I would suggest to create one specific task for each customer. The idea with the ext.customer property is a good way to manage all tasks related to a customer. Then you can create tasks that trigger all customer specific tasks like this:

task buildAllUKTP{
    dependsOn tasks.withType(AbstractArchive).matching{it.customer == 'UKTP'}
}

running buildAllUKTP will now trigger all archive tasks (zips, jars, wars, ears) which have the customer property set to “UKTP”

I dont understand what you mean by “if condition expressions in the war closure were ok: then I would have to state the implicit dependencies there elsewhere as explicit dependencies?”

ueberarchives are archives that contain archives e.g. a ear or a war that includes other archives like jars.

hope that helped,

cheers, René


(Carlo Luib-Finetti) #7

Hi Rene,

thanks for your trial to understand my problem.

Indeed I have a true multiproject build (about 90 projects are contributing to the end artifacts). The only problem I’m having is to make this build custom aware. That is: if I execute the build for customer A, then I have to pack some additional, customer specific Jars into the war (or to the client zip or some other archive), and if it is executed for customer B it needs to pack some other Jars. 90% percent of all the built jars are common to all customers.

So I need some logic to select those customer related jars. Indeed, my first idea was with the -P commandline parameter. But I was looking for a more elegant solution. So I tried with the idea of creating special tasks in the main build file, just to have a distinguished exection environment. I first tried this with evaluating the task graph, then thought it would be simpler just to use a property.

So, in effect, evaluating this simple property in the definition of the war dependencies, I get what I want and I can get rid of those curious build tasks I tried above. And if I’m doing good, I will create those customer specific archiving tasks you spoke of.