Maven POM DSL
01 April 2020
After 8 delusional years of thinking that Gradle can become the best build system on the planet I realized that Maven is the way to go. It has a simple DSL that everyone can gasp within minutes, unlike Gradle, with its fancy Groovy and Kotlin DSL. A full Turing language for your build script is overrated. Ant and Maven worked perfectly fine, why reinvent the wheel? I’m proud to announce that our next big project at Nokee will be a Maven Project Object Model (POM) DSL  support for Gradle. It will ease your transition off Gradle toward Maven. If you are a native developer, have a look at NAR plugin for Maven. It even supports cross-compilation, imagine that!
Pretty much how you would expect it to work.
The plugin reads a standard
pom.xml file and configures your Gradle build accordingly.
It’s that simple.
Forget about the Groovy DSL or even the new, and fancy, Kotlin DSL.
Those are things of the past, long live the POM DSL.
|Don’t go on thinking that Gradle executes a full Maven instance inside the daemon… that would be pure madness.|
I get it, you are a smart developer and want to know how Gradle will behave with this new (old?) DSL.
The following shows the
pom.xml concept mapping to Gradle:
packaging: The values
warmap to the Gradle core plugins
warrespectively. The plugin ignores all other packaging values.
pom.xmlfiles discovery starts from the root POM file, and they all participate in the Gradle multi-project configuration.
properties: The plugin maps the key/value pairs into the Gradle project’s extra properties.
dependencies: It maps each dependency scope to their equivalent Gradle configuration, i.e.
compileOnly, etc. The plugin will also wire local project dependency as you would expect. Finally, each dependency will be subject to the constraint imposed by the
parent: During the configuration phase, it creates an effective POM by merging all the parent POM information for each project.
The other informational tags, i.e.
You can get started with those three easy steps:
From the command line, inside a POM DSL enabled project, simply execute Gradle as you would normally do:
$ gradle assemble BUILD SUCCESSFUL 2 actionable tasks: 2 executed
I’m glad you asked, and the short answer is everything!  What? Don’t you believe me? Fair enough, let’s go through some examples together. Feel free to follow along. We will be using these samples. First things first, let’s clone the repository:
$ git clone https://github.com/nokeedev/gradle-pom-dsl ... $ cd gradle-pom-dsl/src/docs/samples $ ls -1 1.basic-maven-example 2.intermediate-maven-example 3.multi-module-maven-example README.adoc
All the basic models,
modules, etc. are used for configuring the equivalent Gradle model.
We can see it in action using the
projects tasks, as shown below:
$ cd 3.multi-module-maven-example $ gradle projects > Task :projects ------------------------------------------------------------ Root project - This pom acts as the parent pom for the entire project. ------------------------------------------------------------ Root project 'parent' - This pom acts as the parent pom for the entire project. +--- Project ':business' - Business Layer Project. | +--- Project ':business:api' - Business Layer API. | \--- Project ':business:impl' - Business Implementation. +--- Project ':data' - Sample Implementation of the Business Layer. | +--- Project ':data:api' - Data Access API. | \--- Project ':data:impl' - Data Access Implementation. \--- Project ':model' - Model Layer Project. To see a list of the tasks of a project, run gradle <project-path>:tasks For example, try running gradle :business:tasks BUILD SUCCESSFUL 1 actionable task: 1 executed
Did you see that?
The structure of the project, as well as the description, are pulled directly from the
Do you need to exclude transitive dependencies?
Picture the following
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <!-- ... --> <dependencies> <!-- ... --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate</artifactId> <version>3.2.5.ga</version> <exclusions> <exclusion> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <!-- ... --> </project>
Pretty typical, right? Now, let’s see it in action:
$ cd ../2.intermediate-maven-example $ gradle build --scan BUILD SUCCESSFUL 4 actionable tasks: 4 executed Publishing a build scan to scans.gradle.com requires accepting the Gradle Terms of Service defined at https://gradle.com/terms-of-service. Do you accept these terms? [yes, no] yes Gradle Terms of Service accepted. Publishing build scan... https://gradle.com/s/hj73vryn73y74
Did I mention that build scans also work?
Speaking of which, let’s look at the dependency insight report.
We can see the artifact
javax.transaction:jta wasn’t pulled from Maven Central.
So far, the features aren’t that impressive. How about a multi-module project with parent POM configuration? The following example is exactly that:
$ cd ../3.multi-module-maven-example $ gradle check --scan BUILD SUCCESSFUL 11 actionable tasks: 11 executed Publishing a build scan to scans.gradle.com requires accepting the Gradle Terms of Service defined at https://gradle.com/terms-of-service. Do you accept these terms? [yes, no] yes Gradle Terms of Service accepted. Publishing build scan... https://gradle.com/s/tbul7lnihirto
Gradle reports all those tests in the build scan. If you prefer, you can also look at the local reports generated by Gradle.
Are you impressed yet? No? What about…
Everyone’s favourite topic.
The POM DSL plugin automatically applies and configure the
maven-publish plugin free of charge:
It publishes everything, see for yourself:
$ tree ~/.m2/repository ~/.m2/repository └── com └── example └── maven └── layering ├── example-multi-module-business │ ├── 0.0.1-SNAPSHOT │ │ ├── example-multi-module-business-0.0.1-SNAPSHOT.pom │ │ └── maven-metadata-local.xml │ └── maven-metadata-local.xml ├── example-multi-module-business-api │ ├── 0.0.1-SNAPSHOT │ │ ├── example-multi-module-business-api-0.0.1-SNAPSHOT.jar │ │ ├── example-multi-module-business-api-0.0.1-SNAPSHOT.module │ │ ├── example-multi-module-business-api-0.0.1-SNAPSHOT.pom │ │ └── maven-metadata-local.xml │ └── maven-metadata-local.xml ├── example-multi-module-business-impl │ ├── 0.0.1-SNAPSHOT │ │ ├── example-multi-module-business-impl-0.0.1-SNAPSHOT.jar │ │ ├── example-multi-module-business-impl-0.0.1-SNAPSHOT.module │ │ ├── example-multi-module-business-impl-0.0.1-SNAPSHOT.pom │ │ └── maven-metadata-local.xml │ └── maven-metadata-local.xml ├── example-multi-module-data │ ├── 0.0.1-SNAPSHOT │ │ ├── example-multi-module-data-0.0.1-SNAPSHOT.pom │ │ └── maven-metadata-local.xml │ └── maven-metadata-local.xml ├── example-multi-module-data-api │ ├── 0.0.1-SNAPSHOT │ │ ├── example-multi-module-data-api-0.0.1-SNAPSHOT.jar │ │ ├── example-multi-module-data-api-0.0.1-SNAPSHOT.module │ │ ├── example-multi-module-data-api-0.0.1-SNAPSHOT.pom │ │ └── maven-metadata-local.xml │ └── maven-metadata-local.xml ├── example-multi-module-data-impl │ ├── 0.0.1-SNAPSHOT │ │ ├── example-multi-module-data-impl-0.0.1-SNAPSHOT.jar │ │ ├── example-multi-module-data-impl-0.0.1-SNAPSHOT.module │ │ ├── example-multi-module-data-impl-0.0.1-SNAPSHOT.pom │ │ └── maven-metadata-local.xml │ └── maven-metadata-local.xml ├── example-multi-module-model │ ├── 0.0.1-SNAPSHOT │ │ ├── example-multi-module-model-0.0.1-SNAPSHOT.jar │ │ ├── example-multi-module-model-0.0.1-SNAPSHOT.module │ │ ├── example-multi-module-model-0.0.1-SNAPSHOT.pom │ │ └── maven-metadata-local.xml │ └── maven-metadata-local.xml └── parent ├── 0.0.1-SNAPSHOT │ ├── maven-metadata-local.xml │ └── parent-0.0.1-SNAPSHOT.pom └── maven-metadata-local.xml 20 directories, 34 files
That’s not even a question. Have you been following? Maybe this cat picture using the POM DSL plugin will put you at ease.
I can surely try. I had my team prepare a visual for your next meeting to help convince everyone:
Based on a study we did over the weekend, 1 out of 10 participants recommends the POM DSL over the current Groovy and Kotlin DSL. Equally, we made the breakthrough discovery that you can’t trust 9 people out of 10 that participated in the study, go figure.
All kidding aside, Gradle is exceptionally flexible. It always gets me when I hear "Gradle can’t do X, Y or Z". The truth of the matter being Gradle is the most flexible build system and possibly the only one you will ever need.
Creating a build system for a DSL; or
Creating DSL for a build system?
I realized I could have faked the entire post; after all, it’s April Fool day if you didn’t know. However, I built the entire POM DSL plugin and released the source code on GitHub. All samples shown in this post are real examples and work as demonstrated. Feel free to toy around with the project and keep me posted on what you manage to accomplish. This plugin is a demonstration, and you shouldn’t use the plugin in production. 
--init-scriptcommand line flag.