The need for customizing a build based on a particular environment is a prevalent one. Maven offers an option called build profiles which lets you customize certain aspects of the build but not all. However on the Gradle side of things, there's no such feature found in core, with the argument being that contrasted to what Maven offers as means for defining build files (a limited XML based DSL), Gradle offers not one but two DSLs based on full programming languages. This allows developers use the same control flows they're used to in production and test code but on build files. Also, the argument for profiles typically boils down to "it's just a bunch of if statements over a property".
To wit
- Gradle's Support for Maven POM Profiles
- Gradle Profiles for Multi-Project Spring Boot Applications
- Maven profiles equivalent of Gradle
- Gradle Goodness: Using Properties for Multiple Environments or Profiles
Let's start with that argument and see where it may lead us. Say that an environment can be identified by the mere presence of a property. Say this is a project property. You'd expect this property to be defined in the command line using the -P
flag, for example
$ gradle -Pprod build $ gradle -Ptest build
This property could be checked with simple code as this one
build.gradle
if (project.hasProperty('prod')) { // do what prod needs } else if (project.hasProperty('test')) { // do what test needs }
That seems innocuous and straight forward, but as they say the devil is in the details. Be mindful that "prod" and "dev" could also be defined in gradle.properties
, ~/.gradle/gradle.properties
, a Gradle init script, or as part of the ext
block inside the build file itself. Granted, the use of -P
gives it precedence over the others, so I guess we're good? Moving on, what does "prod" require to do its thing? It may be several things. As the first link in the list above suggests we could group those statements in a separate script. Do the same for all environments. Good. Looks like we're done here. Except
- What if you need more environments? Add more if statements and scripts.
- What if you need to check the actual value of the property? Add more if statements.
- Should the value match exactly? Should it match a regex? Add more code.
- Too many statements and conditions already? Hide them under an utility method and/or script as the 4th link shows.
- What if you need to check for an environment variable or a system property? Yup, you guessed it, add more if statements.
- What if you need to evaluate a matrix, say environment (dev, test, prod) plus JDK (8, 11, ...) plus OS (osx, linux, windows) plus some other conditions? More if statements and scripts.
- What if you have to replicate some of these conditions (or all of them) across several projects? Copy & paste code like we did back on the good old days of Ant build scripts? Of course not! You apply a plugin that takes care of that.
If you've come to the last realization (the need of a custom plugin) then good for you. Now you have a choice: build and maintain the plugin yourself or look for an existing solution out there. If you and your team already have experience writing plugins for Gradle then this can be seen as a weekend project that may need to be updated from time to time. If you have not build a Gradle plugin before, it's not hard but then it's yet another piece of infrastructure code that you have to learn and maintain. If it's a 3rd party plugin built by someone else then you have to check if it works with your setup and monitor its releases for compatibility, just like with every other 3rd party plugin already found in your build. So what is it going to be? There are no right or wrong answers, it all depends on the constraints and requirements of your builds, your team, your organization's processes and rules.
But, should you want to try a 3rd party plugin then I suggest you to have a look at the Kordamp profiles plugin. The goals of the Kordamp Gradle Plugin suite are
- Provide a central point of configuration that can be reused by multiple plugins.
- Provide a recognizable build structure across different projects.
- Deliver a DSL inspired by settings found in the Maven POM syntax.
- Reduce the need to have custom programmatic code in build scripts.
For these reasons the profiles-gradle-plugin offers a DSL that closely matches what you find in the Maven build profiles feature. There are 5 types of profile activations (property, jdk, os, file, and custom). The last one allows any condition to be used if the other 4 are not enough. Yes, that breaks the last point of reduced used of programmatic code but there will be times when such extension is needed. Profiles can customize virtually any aspects of a build except for plugins applied via plugins { }
or buildscript { }
, however Kordamp offers an answer to that as well, which will be discussed in another post.
In closing, are profiles really needed in Gradle? Take a look at your builds and decide.
Keep on coding!
Trackbacks/Pingbacks